omega restructuring + a bunch of changes i have made over the last weeks
This commit is contained in:
@@ -16,7 +16,7 @@ packages/
|
||||
|
||||
## Key Components
|
||||
|
||||
To understand the main components of the architecture you canr efer to `architecture.md`.
|
||||
To understand the main components of the architecture you can refer to `docs/architecture.md`.
|
||||
|
||||
## Require clarification and plan approval before making code changes
|
||||
|
||||
|
||||
9319
Cargo.lock
generated
9319
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
10
Cargo.toml
10
Cargo.toml
@@ -1,10 +0,0 @@
|
||||
[workspace]
|
||||
members = [
|
||||
"packages/ac_mize_macros",
|
||||
"packages/marts",
|
||||
"packages/mize",
|
||||
"packages/ppc",
|
||||
"packages/spacetime",
|
||||
"packages/generated",
|
||||
]
|
||||
resolver = "2"
|
||||
10
apps/obsidian-plugin/manifest.json
Normal file
10
apps/obsidian-plugin/manifest.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"id": "mize",
|
||||
"name": "Mize",
|
||||
"version": "0.1.0",
|
||||
"minAppVersion": "1.5.0",
|
||||
"main": "main.js",
|
||||
"author": "c2vi",
|
||||
"isDesktopOnly": true,
|
||||
"description": "Turns Obsidian into a full Mize Explorer"
|
||||
}
|
||||
61
apps/obsidian-plugin/mod.rs
Normal file
61
apps/obsidian-plugin/mod.rs
Normal file
@@ -0,0 +1,61 @@
|
||||
use dioxus::prelude::*;
|
||||
use marts::ClientAuth;
|
||||
use mize::Mize;
|
||||
use wasm_bindgen::prelude::*;
|
||||
use serde::{Serialize, Deserialize};
|
||||
use mize::{MizeResult, mize_part, MizePart, MizePartCreate, error::IntoMizeResultJsValue};
|
||||
use mize::platform::js::JsMize;
|
||||
|
||||
|
||||
// console_log macro
|
||||
#[wasm_bindgen]
|
||||
extern "C" {
|
||||
#[wasm_bindgen(js_namespace = console)]
|
||||
fn log(s: &str);
|
||||
}
|
||||
|
||||
macro_rules! console_log {
|
||||
// Note that this is using the `log` function imported above during
|
||||
// `bare_bones`
|
||||
($($t:tt)*) => ({ log(&format_args!($($t)*).to_string())})
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn test_fn(mize: &mut JsMize) {
|
||||
let mize = mize.inner();
|
||||
console_log!("test fn...");
|
||||
|
||||
if let Err(e) = try_test_fn(mize) {
|
||||
console_log!("err: {:?}", e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fn try_test_fn(mize: &mut Mize) -> MizeResult<()> {
|
||||
//let auth = mize.get_part_native::<ClientAuth>("client_auth")?;
|
||||
//console_log!("secrets: {:?}", auth.secrets());
|
||||
|
||||
let parts = mize.parts.lock().unwrap();
|
||||
for part in parts.keys() {
|
||||
console_log!("part: {}", part)
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn part_obsidian_init(mize: &mut JsMize, plugin: JsValue, app: JsValue) {
|
||||
let mut mize = mize.inner();
|
||||
if let Err(e) = obsidian_mize_main(&mut mize, plugin, app) {
|
||||
console_log!("Failed to start the obsidian mize part: {:?}", e);
|
||||
}
|
||||
}
|
||||
|
||||
fn obsidian_mize_main(mize: &mut Mize, plugin: JsValue, app: JsValue) -> MizeResult<()> {
|
||||
|
||||
marts::part_obsidian(mize, plugin, app)?;
|
||||
marts::part_client_auth(mize)?;
|
||||
marts::spacetime(mize)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
173
apps/obsidian-plugin/plugin.ts
Normal file
173
apps/obsidian-plugin/plugin.ts
Normal file
@@ -0,0 +1,173 @@
|
||||
import { Plugin, ItemView, TFile, Modal } from "obsidian";
|
||||
import wasmBinary from "./rust_dist/ppc_bg.wasm";
|
||||
import * as wasmNamespace from "./rust_dist/ppc";
|
||||
import { c2vi_obsidian_canvas_patch } from "@ppc/marts";
|
||||
import { task_obsidian_otask_parse } from "@ppc/marts";
|
||||
|
||||
class Mize {
|
||||
jsInstance: wasmNamespace.JsMize;
|
||||
jsInstancePtr: number;
|
||||
wasmModule: any;
|
||||
|
||||
constructor(wasmModule: any, jsInstance: wasmNamespace.JsMize) {
|
||||
this.jsInstance = jsInstance
|
||||
this.jsInstancePtr = jsInstance.__wbg_ptr;
|
||||
this.wasmModule = wasmModule
|
||||
}
|
||||
|
||||
test() {
|
||||
this.wasmModule.test_fn(this.jsInstance);
|
||||
}
|
||||
|
||||
get_part_native(name: string) {
|
||||
if (name == "obsidian") {
|
||||
return this.obsidianPlugin
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function mize_init() {
|
||||
|
||||
// init the wasm_bindgen module
|
||||
const wasmModule = await import("./rust_dist/ppc.js");
|
||||
await wasmModule.default({ module_or_path: wasmBinary });
|
||||
wasmModule.initSync();
|
||||
const jsInstance = await wasmModule.new_js_instance(JSON.stringify({
|
||||
auth: {
|
||||
client: {
|
||||
issuer: "https://auth.ppc.social",
|
||||
client_id: "363768685120258051",
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
||||
jsInstance.init();
|
||||
console.log("jsInstance: ", jsInstance)
|
||||
|
||||
let mize = new Mize(wasmModule, jsInstance);
|
||||
|
||||
console.log("jsInstance: ", jsInstance)
|
||||
console.log("mize.jsInstance: ", mize.jsInstance)
|
||||
|
||||
// set global mize
|
||||
window.mize = mize;
|
||||
console.log("window.mize.jsInstance: ", window.mize.jsInstance)
|
||||
|
||||
return mize;
|
||||
}
|
||||
|
||||
export default class MizeObsidianPlugin extends Plugin {
|
||||
async onload() {
|
||||
|
||||
let mize = await mize_init();
|
||||
console.log("mize.jsInstance: ", mize.jsInstance)
|
||||
mize.wasmModule.part_obsidian_init(mize.jsInstance, this, this.app);
|
||||
console.log("mize.jsInstance: ", mize.jsInstance)
|
||||
mize.obsidianPlugin = this;
|
||||
console.log("mize.jsInstance: ", mize.jsInstance)
|
||||
|
||||
// c2vi part which patches canvas movement
|
||||
c2vi_obsidian_canvas_patch(mize);
|
||||
|
||||
task_obsidian_otask_parse(mize);
|
||||
|
||||
this.registerView("test-view", (leaf) => new TestView(leaf, this));
|
||||
|
||||
this.addCommand({
|
||||
id: "mize-test-view",
|
||||
name: "Mize TestView",
|
||||
callback: async () => {
|
||||
const leaf = this.app.workspace.getLeaf(true);
|
||||
await leaf.setViewState({
|
||||
type: "test-view",
|
||||
active: true,
|
||||
});
|
||||
this.app.workspace.revealLeaf(leaf);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
onunload() {
|
||||
this.app.workspace.detachLeavesOfType("ppc-app-view");
|
||||
}
|
||||
|
||||
showDeviceCode(code: string, verificationUri: string) {
|
||||
const modal = new Modal(this.app);
|
||||
|
||||
modal.onOpen = () => {
|
||||
const { contentEl } = modal;
|
||||
|
||||
contentEl.createEl("h2", { text: "Device Login" });
|
||||
|
||||
// URL
|
||||
contentEl.createEl("p", {
|
||||
text: "Open this URL:"
|
||||
});
|
||||
|
||||
const urlContainer = contentEl.createDiv({
|
||||
cls: "device-login-row"
|
||||
});
|
||||
|
||||
const urlInput = urlContainer.createEl("input", {
|
||||
type: "text",
|
||||
value: verificationUri,
|
||||
});
|
||||
|
||||
urlInput.readOnly = true;
|
||||
urlInput.style.width = "100%";
|
||||
|
||||
urlContainer.createEl("button", {
|
||||
text: "Copy"
|
||||
}).onclick = async () => {
|
||||
await navigator.clipboard.writeText(verificationUri);
|
||||
new Notice("URL copied to clipboard");
|
||||
};
|
||||
|
||||
// Code
|
||||
contentEl.createEl("p", {
|
||||
text: "Enter this code:"
|
||||
});
|
||||
|
||||
const codeInput = contentEl.createEl("input", {
|
||||
type: "text",
|
||||
value: code,
|
||||
});
|
||||
|
||||
codeInput.readOnly = true;
|
||||
codeInput.style.width = "100%";
|
||||
codeInput.select();
|
||||
};
|
||||
|
||||
modal.open();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
export class TestView extends ItemView {
|
||||
constructor(leaf: WorkspaceLeaf, plugin: PPCObsidianPlugin) {
|
||||
super(leaf);
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
getViewType(): string {
|
||||
return "ppc-app-view";
|
||||
}
|
||||
|
||||
getDisplayText(): string {
|
||||
return "PPC App";
|
||||
}
|
||||
|
||||
override async onOpen() {
|
||||
this.root = document.createElement("div");
|
||||
this.root.id = "ppc-app-root";
|
||||
this.containerEl.children[1].appendChild(this.root);
|
||||
|
||||
this.plugin.mize.wasmModule.show_main_ui();
|
||||
}
|
||||
|
||||
override async onClose() {
|
||||
this.root.remove();
|
||||
}
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
# Source code architecture
|
||||
|
||||
## 1. Core Mize Framework (`packages/mize/`)
|
||||
|
||||
**Architecture layers:**
|
||||
- `core/`: Platform-independent logic
|
||||
- `config/`: Configuration management
|
||||
- `error/`: Error handling
|
||||
- `id/`: Unique identifier system
|
||||
- `instance/`: Mize runtime instances
|
||||
- `item/`: Filesystem item abstractions
|
||||
- `memstore/`: In-memory storage
|
||||
- `proto/`: Protocol definitions
|
||||
- `types/`: Core type definitions
|
||||
|
||||
- `platform/`: Platform-specific implementations
|
||||
- `os/`: Native OS integration
|
||||
- `wasm/`: WebAssembly support
|
||||
- `bare/`: Bare metal targets
|
||||
- `jvm/`: Java Virtual Machine integration
|
||||
|
||||
### 2. Marts (`packages/marts/`)
|
||||
- **Language**: Rust + TypeScript
|
||||
- **Purpose**: Collection of Mize framework parts and utilities
|
||||
- **Key features**:
|
||||
- CLI tools
|
||||
- JavaScript/TypeScript integration
|
||||
- Habitica integration
|
||||
- Deno-based scripting support
|
||||
|
||||
## 3. MME - Mize Module Environment (`packages/mme/`)
|
||||
- **Language**: Rust
|
||||
- **Purpose**: Module execution environment
|
||||
- **Key features**:
|
||||
- Cross-platform module loading
|
||||
- Qt and Slint UI support
|
||||
- Web view integration (tao/wry)
|
||||
- Command system (comandr)
|
||||
|
||||
## 4. PPC - Platform Components (`packages/ppc/`)
|
||||
- **Language**: Rust + TypeScript
|
||||
- **Purpose**: Platform-specific implementations
|
||||
- **Targets**: Obsidian plugin, OS integration
|
||||
|
||||
## 5. Vic - Victor CLI (`packages/vic/`)
|
||||
- **Language**: Rust
|
||||
- **Purpose**: Command-line interface tool
|
||||
- **Features**: Build, run, test, and manage Mize applications
|
||||
74
deno.lock
generated
74
deno.lock
generated
@@ -11,6 +11,7 @@
|
||||
"npm:esbuild@*": "0.27.3",
|
||||
"npm:esbuild@~0.25.5": "0.25.12",
|
||||
"npm:esbuild@~0.27.3": "0.27.3",
|
||||
"npm:matrix-js-sdk@42": "42.0.0",
|
||||
"npm:mdast@3": "3.0.0",
|
||||
"npm:obsidian@^1.12.2": "1.12.3_@codemirror+state@6.5.0_@codemirror+view@6.38.6",
|
||||
"npm:obsidian@^1.12.3": "1.12.3_@codemirror+state@6.5.0_@codemirror+view@6.38.6",
|
||||
@@ -45,6 +46,9 @@
|
||||
}
|
||||
},
|
||||
"npm": {
|
||||
"@babel/runtime@8.0.0": {
|
||||
"integrity": "sha512-sL6cvO2IfkSu/iU+zs2S/w01B7A8V7suXSIKEN4hPFFdZoiPGxrj5pAG0lCaqLWiEIrjKzdznIWuaLcxPR53qw=="
|
||||
},
|
||||
"@codemirror/state@6.5.0": {
|
||||
"integrity": "sha512-MwBHVK60IiIHDcoMet78lxt6iw5gJOGSbNbOIVBHWVXIH4/Nq1+GQgLLGgI1KlnN86WDXsPudVaqYHKBIx7Eyw==",
|
||||
"dependencies": [
|
||||
@@ -332,6 +336,9 @@
|
||||
"@marijn/find-cluster-break@1.0.2": {
|
||||
"integrity": "sha512-l0h88YhZFyKdXIFNfSWpyjStDjGHwZ/U7iobcK1cQQD8sejsONdQtTVU+1wVN1PBw40PiiHB1vA5S7VTfQiP9g=="
|
||||
},
|
||||
"@matrix-org/matrix-sdk-crypto-wasm@18.3.1": {
|
||||
"integrity": "sha512-VRjWhE1UgHnPpJ3b9B5+8z71ZC/HICFngPPFIN6ktzmUBKI5RusPujzbAQUoB3CgZ0yU58L99AfSQS4YTztSWw=="
|
||||
},
|
||||
"@types/codemirror@5.60.8": {
|
||||
"integrity": "sha512-VjFgDF/eB+Aklcy15TtOTLQeMjTo07k7KAjql8OK5Dirr7a6sJY4T1uVBDuTVG9VEmn1uUsohOpYnVfgC6/jyw==",
|
||||
"dependencies": [
|
||||
@@ -347,6 +354,9 @@
|
||||
"@types/estree@1.0.8": {
|
||||
"integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="
|
||||
},
|
||||
"@types/events@3.0.3": {
|
||||
"integrity": "sha512-trOc4AAUThEz9hapPtSd7wf5tiQKvTtu5b371UxXdTuqzIh0ArcRspRP0i0Viu+LXstIQ1z96t1nsPxT9ol01g=="
|
||||
},
|
||||
"@types/mdast@4.0.4": {
|
||||
"integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==",
|
||||
"dependencies": [
|
||||
@@ -365,9 +375,21 @@
|
||||
"@types/unist@3.0.3": {
|
||||
"integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q=="
|
||||
},
|
||||
"another-json@0.2.0": {
|
||||
"integrity": "sha512-/Ndrl68UQLhnCdsAzEXLMFuOR546o2qbYRqCglaNHbjXrwG1ayTcdwr3zkSGOGtGXDyR5X9nCFfnyG2AFJIsqg=="
|
||||
},
|
||||
"bail@2.0.2": {
|
||||
"integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw=="
|
||||
},
|
||||
"base-x@5.0.1": {
|
||||
"integrity": "sha512-M7uio8Zt++eg3jPj+rHMfCC+IuygQHHCOU+IYsVtik6FWjuYpVt/+MRKcgsAMHh8mMFAwnB+Bs+mTrFiXjMzKg=="
|
||||
},
|
||||
"bs58@6.0.0": {
|
||||
"integrity": "sha512-PD0wEnEYg6ijszw/u8s+iI3H17cTymlrwkKhDhPZq+Sokl3AU4htyBFTjAeNAlCCmg0f53g6ih3jATyCKftTfw==",
|
||||
"dependencies": [
|
||||
"base-x"
|
||||
]
|
||||
},
|
||||
"builtin-modules@5.0.0": {
|
||||
"integrity": "sha512-bkXY9WsVpY7CvMhKSR6pZilZu9Ln5WDrKVBUXf2S443etkmEO4V58heTecXcUIsNsi4Rx8JUO4NfX1IcQl4deg=="
|
||||
},
|
||||
@@ -377,6 +399,9 @@
|
||||
"character-entities@2.0.2": {
|
||||
"integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ=="
|
||||
},
|
||||
"content-type@2.0.0": {
|
||||
"integrity": "sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ=="
|
||||
},
|
||||
"crelt@1.0.6": {
|
||||
"integrity": "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g=="
|
||||
},
|
||||
@@ -470,6 +495,9 @@
|
||||
"escape-string-regexp@5.0.0": {
|
||||
"integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw=="
|
||||
},
|
||||
"events@3.3.0": {
|
||||
"integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q=="
|
||||
},
|
||||
"extend@3.0.2": {
|
||||
"integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="
|
||||
},
|
||||
@@ -485,15 +513,47 @@
|
||||
"github-slugger@2.0.0": {
|
||||
"integrity": "sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw=="
|
||||
},
|
||||
"is-network-error@1.3.2": {
|
||||
"integrity": "sha512-PhBY86zaxNZUuWP6h13Vu5oFe0XY6/UlKzQnYFELzGVHygP3MxmvTfYSG7GN3aIab/iWudSMgjSnG9Dq+nHrgA=="
|
||||
},
|
||||
"is-plain-obj@4.1.0": {
|
||||
"integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg=="
|
||||
},
|
||||
"loglevel@1.9.2": {
|
||||
"integrity": "sha512-HgMmCqIJSAKqo68l0rS2AanEWfkxaZ5wNiEFb5ggm08lDs9Xl2KxBlX3PTcaD2chBM1gXAYf491/M2Rv8Jwayg=="
|
||||
},
|
||||
"longest-streak@3.1.0": {
|
||||
"integrity": "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g=="
|
||||
},
|
||||
"markdown-table@3.0.4": {
|
||||
"integrity": "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw=="
|
||||
},
|
||||
"matrix-events-sdk@0.0.1": {
|
||||
"integrity": "sha512-1QEOsXO+bhyCroIe2/A5OwaxHvBm7EsSQ46DEDn8RBIfQwN5HWBpFvyWWR4QY0KHPPnnJdI99wgRiAl7Ad5qaA=="
|
||||
},
|
||||
"matrix-js-sdk@42.0.0": {
|
||||
"integrity": "sha512-i3SnhdkgnKA7O0iVlaZCo96GGaIZ0FGD93LQ34N2+Hj/kaC7Ffbch7OEkwYA//mYGCKRAMxduBacoy8WscfqVw==",
|
||||
"dependencies": [
|
||||
"@babel/runtime",
|
||||
"@matrix-org/matrix-sdk-crypto-wasm",
|
||||
"another-json",
|
||||
"bs58",
|
||||
"content-type",
|
||||
"loglevel",
|
||||
"matrix-events-sdk",
|
||||
"matrix-widget-api",
|
||||
"p-retry",
|
||||
"sdp-transform",
|
||||
"unhomoglyph"
|
||||
]
|
||||
},
|
||||
"matrix-widget-api@1.17.0": {
|
||||
"integrity": "sha512-5FHoo3iEP3Bdlv5jsYPWOqj+pGdFQNLWnJLiB0V7Ygne7bb+Gsj3ibyFyHWC6BVw+Z+tSW4ljHpO17I9TwStwQ==",
|
||||
"dependencies": [
|
||||
"@types/events",
|
||||
"events"
|
||||
]
|
||||
},
|
||||
"mdast-util-find-and-replace@3.0.2": {
|
||||
"integrity": "sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==",
|
||||
"dependencies": [
|
||||
@@ -886,6 +946,12 @@
|
||||
"moment"
|
||||
]
|
||||
},
|
||||
"p-retry@8.0.0": {
|
||||
"integrity": "sha512-kFVqH1HxOHp8LupNsOys7bSV09VYTRLxarH/mokO4Rqhk6wGi70E0jh4VzvVGXfEVNggHoHLAMWsQqHyU1Ey9A==",
|
||||
"dependencies": [
|
||||
"is-network-error"
|
||||
]
|
||||
},
|
||||
"remark-frontmatter@5.0.0": {
|
||||
"integrity": "sha512-XTFYvNASMe5iPN0719nPrdItC9aU0ssC4v14mH1BCi1u0n1gAocqcujWUrByftZTbLhRtiKRyjYTSIOcr69UVQ==",
|
||||
"dependencies": [
|
||||
@@ -923,6 +989,10 @@
|
||||
"unified"
|
||||
]
|
||||
},
|
||||
"sdp-transform@3.0.0": {
|
||||
"integrity": "sha512-gfYVRGxjHkGF2NPeUWHw5u6T/KGFtS5/drPms73gaSuMaVHKCY3lpLnGDfswVQO0kddeePoti09AwhYP4zA8dQ==",
|
||||
"bin": true
|
||||
},
|
||||
"style-mod@4.1.3": {
|
||||
"integrity": "sha512-i/n8VsZydrugj3Iuzll8+x/00GH2vnYsk1eomD8QiRrSAeW6ItbCQDtfXCeJHd0iwiNagqjQkvpvREEPtW3IoQ=="
|
||||
},
|
||||
@@ -933,6 +1003,9 @@
|
||||
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
||||
"bin": true
|
||||
},
|
||||
"unhomoglyph@1.0.6": {
|
||||
"integrity": "sha512-7uvcWI3hWshSADBu4JpnyYbTVc7YlhF5GDW/oPD5AxIxl34k4wXR3WDkPnzLxkN32LiTCTKMQLtKVZiwki3zGg=="
|
||||
},
|
||||
"unified@11.0.5": {
|
||||
"integrity": "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==",
|
||||
"dependencies": [
|
||||
@@ -1019,6 +1092,7 @@
|
||||
"npm:@flowershow/remark-wiki-link@^3.4.0",
|
||||
"npm:builtin-modules@5",
|
||||
"npm:esbuild@~0.27.3",
|
||||
"npm:matrix-js-sdk@42",
|
||||
"npm:mdast@3",
|
||||
"npm:obsidian@^1.12.2",
|
||||
"npm:remark-frontmatter@5",
|
||||
|
||||
0
mize → docs/architecture.md
Executable file → Normal file
0
mize → docs/architecture.md
Executable file → Normal file
6
flake.lock
generated
6
flake.lock
generated
@@ -72,11 +72,11 @@
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1774709303,
|
||||
"narHash": "sha256-D3Q07BbIA2KnTcSXIqqu9P586uWxN74zNoCH3h2ESHg=",
|
||||
"lastModified": 1782467914,
|
||||
"narHash": "sha256-pGvFkM8N0xEkIIXDe5YYfbEAvHrk4IxBrjB/x8OomhE=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "8110df5ad7abf5d4c0f6fb0f8f978390e77f9685",
|
||||
"rev": "e73de5be04e0eff4190a1432b946d469c794e7b4",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
||||
27
flake.nix
27
flake.nix
@@ -1,6 +1,5 @@
|
||||
{
|
||||
inputs = {
|
||||
# nixpkgs 35.11 still contains rust 1.73
|
||||
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
||||
|
||||
fenix = {
|
||||
@@ -30,7 +29,6 @@
|
||||
let
|
||||
pkgs = nixpkgs.legacyPackages.${system};
|
||||
|
||||
#os
|
||||
osToolchain = fenix.packages.${system}.latest.toolchain;
|
||||
osCrane = (crane.mkLib pkgs).overrideToolchain osToolchain;
|
||||
|
||||
@@ -177,6 +175,10 @@ in {
|
||||
pkgs.pkg-config
|
||||
pkgs.gobject-introspection
|
||||
pkgs.gobject-introspection.dev
|
||||
pkgs.systemd
|
||||
pkgs.rsync
|
||||
pkgs.deno
|
||||
pkgs.openssh
|
||||
|
||||
# thanks to: https://github.com/NixOS/nixpkgs/pull/496279/changes
|
||||
(pkgs.buildWasmBindgenCli rec {
|
||||
@@ -233,7 +235,7 @@ in {
|
||||
];
|
||||
|
||||
};
|
||||
default = pkgs.mkShell {
|
||||
default = pkgs.mkShell rec {
|
||||
|
||||
MIZE_BUILD_CONFIG = let
|
||||
settingsFormat = pkgs.formats.toml { };
|
||||
@@ -252,6 +254,15 @@ in {
|
||||
pkgs.pkg-config
|
||||
pkgs.gobject-introspection
|
||||
pkgs.gobject-introspection.dev
|
||||
pkgs.systemd
|
||||
pkgs.rsync
|
||||
pkgs.openssh
|
||||
pkgs.git
|
||||
|
||||
# to build ../grid
|
||||
pkgs.rustPlatform.bindgenHook
|
||||
pkgs.pipewire.dev
|
||||
pkgs.alsa-lib.dev
|
||||
|
||||
# thanks to: https://github.com/NixOS/nixpkgs/pull/496279/changes
|
||||
(pkgs.buildWasmBindgenCli rec {
|
||||
@@ -305,7 +316,17 @@ in {
|
||||
harfbuzz
|
||||
librsvg
|
||||
libsoup_3
|
||||
|
||||
# to build ../grid
|
||||
libxkbcommon
|
||||
alsa-lib
|
||||
libgcc.lib
|
||||
libxcb
|
||||
pipewire
|
||||
wayland
|
||||
];
|
||||
|
||||
LIBRARY_PATH = LD_LIBRARY_PATH;
|
||||
|
||||
};
|
||||
};
|
||||
|
||||
@@ -9,3 +9,5 @@ proc-macro = true
|
||||
[dependencies]
|
||||
quote = "1.0.44"
|
||||
syn = "2.0.114"
|
||||
proc-macro2 = "1.0"
|
||||
ciborium = "0.2"
|
||||
1
macros/src/data.rs
Normal file
1
macros/src/data.rs
Normal file
@@ -0,0 +1 @@
|
||||
|
||||
280
macros/src/lib.rs
Normal file
280
macros/src/lib.rs
Normal file
@@ -0,0 +1,280 @@
|
||||
use proc_macro::TokenStream;
|
||||
use quote::quote;
|
||||
use syn::{Data, DeriveInput, Fields, parse_macro_input};
|
||||
|
||||
#[proc_macro_attribute]
|
||||
pub fn mize_part(attr: TokenStream, item: TokenStream) -> TokenStream {
|
||||
let input = parse_macro_input!(item as DeriveInput);
|
||||
let struct_name = &input.ident;
|
||||
|
||||
// Parse name from attribute
|
||||
let part_name = if !attr.is_empty() {
|
||||
let lit: syn::LitStr = parse_macro_input!(attr);
|
||||
lit.value()
|
||||
} else {
|
||||
struct_name.to_string().to_lowercase()
|
||||
};
|
||||
|
||||
// Verify mize field exists
|
||||
let has_mize = match &input.data {
|
||||
Data::Struct(s) => match &s.fields {
|
||||
Fields::Named(f) => f
|
||||
.named
|
||||
.iter()
|
||||
.any(|field| field.ident.as_ref().map(|i| i == "mize").unwrap_or(false)),
|
||||
_ => false,
|
||||
},
|
||||
_ => false,
|
||||
};
|
||||
|
||||
if !has_mize {
|
||||
return syn::Error::new_spanned(struct_name, "Struct must have a 'mize: Mize' field")
|
||||
.to_compile_error()
|
||||
.into();
|
||||
}
|
||||
|
||||
let part_getter_fn_name_str = format!("make_mize_part_{}", part_name);
|
||||
let part_getter_fn_name = syn::Ident::new(&part_getter_fn_name_str, struct_name.span());
|
||||
|
||||
let expanded = quote! {
|
||||
#input
|
||||
|
||||
// impl the default new impl
|
||||
impl mize::MizePartGenerated for #struct_name {
|
||||
fn name_generated(&self) -> &'static str {
|
||||
#part_name
|
||||
}
|
||||
fn get_mize_generated(&mut self) -> &mut Mize {
|
||||
&mut self.mize
|
||||
}
|
||||
fn as_any_generated(&self) -> &dyn std::any::Any {
|
||||
self
|
||||
}
|
||||
fn as_any_mut_generated(&mut self) -> &mut dyn std::any::Any {
|
||||
self
|
||||
}
|
||||
fn into_any_generated(self: Box<Self>) -> Box<dyn std::any::Any> {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl mize::MizePartCreateGenerated for #struct_name {
|
||||
fn create_generated(mize: Mize) -> Self {
|
||||
Self {
|
||||
mize,
|
||||
.. Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
use mize::instance::MizePartCreateThisSucks;
|
||||
|
||||
impl MizePartCreateThisSucks for #struct_name {}
|
||||
|
||||
// the function to get the part from a .so file
|
||||
#[unsafe(no_mangle)]
|
||||
extern "C" fn #part_getter_fn_name(part: &mut Box<dyn MizePart + Send + Sync>, mut mize: Mize) {
|
||||
#struct_name::create_this_sucks(&mut mize);
|
||||
}
|
||||
|
||||
/*
|
||||
impl std::ops::Deref for mize::MizePartGuard<#struct_name> {
|
||||
type Target = #struct_name;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&*self.part.as_ref().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::DerefMut for mize::MizePartGuard<#struct_name> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut *self.part.as_mut().unwrap()
|
||||
}
|
||||
}
|
||||
*/
|
||||
};
|
||||
|
||||
TokenStream::from(expanded)
|
||||
}
|
||||
|
||||
//////////////////////////////////// data! macro ///////////////////////////////////////
|
||||
// A procedural macro to define data in Nix-style and expand to a `ciborium::Value` (CBOR).
|
||||
//
|
||||
// Add this to your `Cargo.toml`:
|
||||
// ```toml
|
||||
// [lib]
|
||||
// proc-macro = true
|
||||
//
|
||||
// [dependencies]
|
||||
// syn = { version = "2.0", features = ["full", "extra-traits"] }
|
||||
// quote = "1.0"
|
||||
// proc-macro2 = "1.0"
|
||||
// ciborium = "0.2"
|
||||
// ```
|
||||
|
||||
use syn::{Ident, Lit, Token, parse::Parse, parse::ParseStream, punctuated::Punctuated, token};
|
||||
|
||||
// --- AST ---
|
||||
|
||||
#[derive(Debug)]
|
||||
enum NixExpr {
|
||||
Map(Vec<(String, NixExpr)>),
|
||||
List(Vec<NixExpr>),
|
||||
Literal(Literal),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum Literal {
|
||||
String(String),
|
||||
Int(i128),
|
||||
Float(f64),
|
||||
Bool(bool),
|
||||
Null,
|
||||
}
|
||||
|
||||
// --- Code Generation ---
|
||||
|
||||
impl NixExpr {
|
||||
fn to_cbor_tokens(&self) -> proc_macro2::TokenStream {
|
||||
match self {
|
||||
NixExpr::Map(entries) => {
|
||||
let entry_tokens: Vec<_> = entries
|
||||
.iter()
|
||||
.map(|(k, v)| {
|
||||
let key = syn::LitStr::new(k, proc_macro2::Span::call_site());
|
||||
let value_tokens = v.to_cbor_tokens();
|
||||
quote! {
|
||||
(mize::ciborium::Value::Text(#key.to_string()), #value_tokens)
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
quote! {
|
||||
mize::ciborium::Value::Map({
|
||||
let mut m = Vec::<(mize::ciborium::Value, mize::ciborium::Value)>::new();
|
||||
#(m.push(#entry_tokens);)*
|
||||
m
|
||||
})
|
||||
}
|
||||
}
|
||||
NixExpr::List(items) => {
|
||||
let item_tokens: Vec<_> = items.iter().map(|item| item.to_cbor_tokens()).collect();
|
||||
quote! {
|
||||
mize::ciborium::Value::Array({
|
||||
let mut v = Vec::<mize::ciborium::Value>::new();
|
||||
#(v.push(#item_tokens);)*
|
||||
v
|
||||
})
|
||||
}
|
||||
}
|
||||
NixExpr::Literal(lit) => lit.to_cbor_tokens(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Literal {
|
||||
fn to_cbor_tokens(&self) -> proc_macro2::TokenStream {
|
||||
match self {
|
||||
Literal::String(s) => {
|
||||
let s = syn::LitStr::new(s, proc_macro2::Span::call_site());
|
||||
quote! { mize::ciborium::Value::Text(#s.to_string()) }
|
||||
}
|
||||
Literal::Int(i) => quote! { mize::ciborium::Value::Integer(#i) },
|
||||
Literal::Float(f) => quote! { mize::ciborium::Value::Float(#f) },
|
||||
Literal::Bool(b) => quote! { mize::ciborium::Value::Bool(#b) },
|
||||
Literal::Null => quote! { mize::ciborium::Value::Null },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --- Parsing ---
|
||||
|
||||
struct NixKeyValue {
|
||||
key: Ident,
|
||||
_eq: Token![=],
|
||||
value: NixExpr,
|
||||
}
|
||||
|
||||
impl Parse for NixKeyValue {
|
||||
fn parse(input: ParseStream) -> syn::Result<Self> {
|
||||
Ok(NixKeyValue {
|
||||
key: input.parse()?,
|
||||
_eq: input.parse()?,
|
||||
value: input.parse()?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Parse for NixExpr {
|
||||
fn parse(input: ParseStream) -> syn::Result<Self> {
|
||||
// Parse a map: { a = 1; b = 2; }
|
||||
if input.peek(token::Brace) {
|
||||
let content;
|
||||
syn::braced!(content in input);
|
||||
let pairs = Punctuated::<NixKeyValue, Token![;]>::parse_terminated(&content)?;
|
||||
let entries = pairs
|
||||
.into_iter()
|
||||
.map(|kv| (kv.key.to_string(), kv.value))
|
||||
.collect();
|
||||
return Ok(NixExpr::Map(entries));
|
||||
}
|
||||
|
||||
// Parse a list: [ 1, 2, 3 ]
|
||||
if input.peek(token::Bracket) {
|
||||
let content;
|
||||
syn::bracketed!(content in input);
|
||||
let items = Punctuated::<NixExpr, Token![,]>::parse_terminated(&content)?;
|
||||
return Ok(NixExpr::List(items.into_iter().collect()));
|
||||
}
|
||||
|
||||
// Parse literals
|
||||
if input.peek(Lit) {
|
||||
let lit = input.parse::<Lit>()?;
|
||||
return match lit {
|
||||
Lit::Str(s) => Ok(NixExpr::Literal(Literal::String(s.value()))),
|
||||
Lit::Int(i) => Ok(NixExpr::Literal(Literal::Int(i.base10_parse::<i128>()?))),
|
||||
Lit::Float(f) => Ok(NixExpr::Literal(Literal::Float(f.base10_parse::<f64>()?))),
|
||||
Lit::Bool(b) => Ok(NixExpr::Literal(Literal::Bool(b.value))),
|
||||
_ => Err(input.error("Unsupported literal")),
|
||||
};
|
||||
}
|
||||
|
||||
Err(input.error("Expected a Nix expression (map, list, or literal)"))
|
||||
}
|
||||
}
|
||||
|
||||
// --- Top-level AttrSet Parsing ---
|
||||
|
||||
struct NixAttrSet(Vec<NixKeyValue>);
|
||||
|
||||
impl syn::parse::Parse for NixAttrSet {
|
||||
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
|
||||
let pairs = Punctuated::<NixKeyValue, Token![;]>::parse_terminated(input)?;
|
||||
Ok(NixAttrSet(pairs.into_iter().collect()))
|
||||
}
|
||||
}
|
||||
|
||||
// --- Macro Entry ---
|
||||
|
||||
#[proc_macro]
|
||||
pub fn data(input: TokenStream) -> TokenStream {
|
||||
// Try to parse top-level attrset (without outer braces)
|
||||
let expr = syn::parse::<NixAttrSet>(input.clone())
|
||||
.map(|attrset| {
|
||||
NixExpr::Map(
|
||||
attrset.0
|
||||
.into_iter()
|
||||
.map(|kv| (kv.key.to_string(), kv.value))
|
||||
.collect(),
|
||||
)
|
||||
})
|
||||
.or_else(|_| {
|
||||
// fallback: parse legacy behavior (braced map, list, or literal)
|
||||
syn::parse::<NixExpr>(input)
|
||||
});
|
||||
|
||||
let tokens = match expr {
|
||||
Ok(expr) => expr.to_cbor_tokens(),
|
||||
Err(e) => return e.to_compile_error().into(),
|
||||
};
|
||||
TokenStream::from(tokens)
|
||||
}
|
||||
50
marts/Cargo.toml
Normal file
50
marts/Cargo.toml
Normal file
@@ -0,0 +1,50 @@
|
||||
[package]
|
||||
name = "marts"
|
||||
description = "marts = mize_parts - parts collection of the Mize framework"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
clap = { version = "4.5.59", optional = true, features = ["string"] }
|
||||
flume = "0.12.0"
|
||||
futures = "0.3.32"
|
||||
mize = { path = "../mize/", default-features = false, features = [] }
|
||||
tokio = { version = "1.49.0", default-features = false, features = ["time"] }
|
||||
# https://github.com/rscarson/rustyscript/pull/415
|
||||
#rustyscript = { git = 'https://github.com/rscarson/rustyscript', rev = 'e39529eac2c5d37cfcbb7d7d63da7264d7a5afdb', optional = true }
|
||||
#deno_core = { version = "0.380.1", optional = true }
|
||||
reqwest = { version = "0.13.2", features = ["blocking", "json"] }
|
||||
serde = "1.0.228"
|
||||
serde_json = "1.0.149"
|
||||
rusqlite = { version = "0.31", features = ["bundled"], optional = true }
|
||||
dioxus = { version = "0.7.1", optional = true }
|
||||
spacetimedb-sdk = { version = "2.6.0", default-features = false }
|
||||
generated = { path = "./generated" }
|
||||
webbrowser = { version = "1.2.0", optional = true }
|
||||
openidconnect = { version = "4", features = ["reqwest", "rustls-tls"], default-features = false }
|
||||
tracing = "0.1.40"
|
||||
wasm-bindgen = { version = "0.2.84", optional = true }
|
||||
wasm-bindgen-futures = { version = "0.4.49", optional = true }
|
||||
web-sys = { version = "0.3.70", optional = true, features = [
|
||||
"Window",
|
||||
"Storage",
|
||||
"console"
|
||||
]}
|
||||
js-sys = { version = "0.3.70", optional = true }
|
||||
spacetimedb = { version = "2.6.0", features = ["unstable"] }
|
||||
log = "0.4"
|
||||
chrono = { version = "0.4.45", features = [], default-features = false }
|
||||
chrono-tz = { version = "0.10.4", default-features = false }
|
||||
url = "2.5.8"
|
||||
|
||||
[features]
|
||||
default = ["target-os", "desktop", "server", "client"]
|
||||
#default = ["target-js", "target-obsidian"] # to get rust analyzer to analyzer js only parts
|
||||
target-spacetime = [ "mize/target-js", "spacetimedb-sdk/browser" ]
|
||||
target-os = [ "clap", "mize/target-os", "rusqlite", "dioxus", "dep:webbrowser" ]
|
||||
target-js = [ "mize/target-js", "spacetimedb-sdk/browser", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", "js-sys", "generated/target-js", "web-sys/Storage", "web-sys/Window" ]
|
||||
target-obsidian = [ "target-js" ]
|
||||
desktop = ["dioxus/desktop"]
|
||||
client = []
|
||||
server = []
|
||||
clap = ["dep:clap"]
|
||||
@@ -2,7 +2,7 @@
|
||||
"name": "@ppc/marts",
|
||||
"imports": {},
|
||||
"exports": {
|
||||
".": "./index.ts"
|
||||
".": "./src/index.ts"
|
||||
},
|
||||
"tasks": {}
|
||||
}
|
||||
@@ -8,4 +8,7 @@ name = "generated"
|
||||
path = "src/lib.rs"
|
||||
|
||||
[dependencies]
|
||||
spacetimedb-sdk = "2.1.0"
|
||||
spacetimedb-sdk = "2.6.0"
|
||||
|
||||
[features]
|
||||
target-js = [ "spacetimedb-sdk/browser" ]
|
||||
@@ -1,2 +1 @@
|
||||
pub mod spacetime;
|
||||
|
||||
68
marts/generated/src/spacetime/add_thought_reducer.rs
Normal file
68
marts/generated/src/spacetime/add_thought_reducer.rs
Normal file
@@ -0,0 +1,68 @@
|
||||
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
#![allow(unused, clippy::all)]
|
||||
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
||||
|
||||
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
|
||||
#[sats(crate = __lib)]
|
||||
pub(super) struct AddThoughtArgs {
|
||||
pub thought: String,
|
||||
}
|
||||
|
||||
impl From<AddThoughtArgs> for super::Reducer {
|
||||
fn from(args: AddThoughtArgs) -> Self {
|
||||
Self::AddThought {
|
||||
thought: args.thought,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl __sdk::InModule for AddThoughtArgs {
|
||||
type Module = super::RemoteModule;
|
||||
}
|
||||
|
||||
#[allow(non_camel_case_types)]
|
||||
/// Extension trait for access to the reducer `add_thought`.
|
||||
///
|
||||
/// Implemented for [`super::RemoteReducers`].
|
||||
pub trait add_thought {
|
||||
/// Request that the remote module invoke the reducer `add_thought` to run as soon as possible.
|
||||
///
|
||||
/// This method returns immediately, and errors only if we are unable to send the request.
|
||||
/// The reducer will run asynchronously in the future,
|
||||
/// and this method provides no way to listen for its completion status.
|
||||
/// /// Use [`add_thought:add_thought_then`] to run a callback after the reducer completes.
|
||||
fn add_thought(&self, thought: String) -> __sdk::Result<()> {
|
||||
self.add_thought_then(thought, |_, _| {})
|
||||
}
|
||||
|
||||
/// Request that the remote module invoke the reducer `add_thought` to run as soon as possible,
|
||||
/// registering `callback` to run when we are notified that the reducer completed.
|
||||
///
|
||||
/// This method returns immediately, and errors only if we are unable to send the request.
|
||||
/// The reducer will run asynchronously in the future,
|
||||
/// and its status can be observed with the `callback`.
|
||||
fn add_thought_then(
|
||||
&self,
|
||||
thought: String,
|
||||
|
||||
callback: impl FnOnce(&super::ReducerEventContext, Result<Result<(), String>, __sdk::InternalError>)
|
||||
+ Send
|
||||
+ 'static,
|
||||
) -> __sdk::Result<()>;
|
||||
}
|
||||
|
||||
impl add_thought for super::RemoteReducers {
|
||||
fn add_thought_then(
|
||||
&self,
|
||||
thought: String,
|
||||
|
||||
callback: impl FnOnce(&super::ReducerEventContext, Result<Result<(), String>, __sdk::InternalError>)
|
||||
+ Send
|
||||
+ 'static,
|
||||
) -> __sdk::Result<()> {
|
||||
self.imp
|
||||
.invoke_reducer_with_callback(AddThoughtArgs { thought }, callback)
|
||||
}
|
||||
}
|
||||
111
marts/generated/src/spacetime/daily_task_log_table.rs
Normal file
111
marts/generated/src/spacetime/daily_task_log_table.rs
Normal file
@@ -0,0 +1,111 @@
|
||||
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
#![allow(unused, clippy::all)]
|
||||
use super::daily_task_log_type::DailyTaskLog;
|
||||
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
||||
|
||||
/// Table handle for the table `daily_task_log`.
|
||||
///
|
||||
/// Obtain a handle from the [`DailyTaskLogTableAccess::daily_task_log`] method on [`super::RemoteTables`],
|
||||
/// like `ctx.db.daily_task_log()`.
|
||||
///
|
||||
/// Users are encouraged not to explicitly reference this type,
|
||||
/// but to directly chain method calls,
|
||||
/// like `ctx.db.daily_task_log().on_insert(...)`.
|
||||
pub struct DailyTaskLogTableHandle<'ctx> {
|
||||
imp: __sdk::TableHandle<DailyTaskLog>,
|
||||
ctx: std::marker::PhantomData<&'ctx super::RemoteTables>,
|
||||
}
|
||||
|
||||
#[allow(non_camel_case_types)]
|
||||
/// Extension trait for access to the table `daily_task_log`.
|
||||
///
|
||||
/// Implemented for [`super::RemoteTables`].
|
||||
pub trait DailyTaskLogTableAccess {
|
||||
#[allow(non_snake_case)]
|
||||
/// Obtain a [`DailyTaskLogTableHandle`], which mediates access to the table `daily_task_log`.
|
||||
fn daily_task_log(&self) -> DailyTaskLogTableHandle<'_>;
|
||||
}
|
||||
|
||||
impl DailyTaskLogTableAccess for super::RemoteTables {
|
||||
fn daily_task_log(&self) -> DailyTaskLogTableHandle<'_> {
|
||||
DailyTaskLogTableHandle {
|
||||
imp: self.imp.get_table::<DailyTaskLog>("daily_task_log"),
|
||||
ctx: std::marker::PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DailyTaskLogInsertCallbackId(__sdk::CallbackId);
|
||||
pub struct DailyTaskLogDeleteCallbackId(__sdk::CallbackId);
|
||||
|
||||
impl<'ctx> __sdk::Table for DailyTaskLogTableHandle<'ctx> {
|
||||
type Row = DailyTaskLog;
|
||||
type EventContext = super::EventContext;
|
||||
|
||||
fn count(&self) -> u64 {
|
||||
self.imp.count()
|
||||
}
|
||||
fn iter(&self) -> impl Iterator<Item = DailyTaskLog> + '_ {
|
||||
self.imp.iter()
|
||||
}
|
||||
|
||||
type InsertCallbackId = DailyTaskLogInsertCallbackId;
|
||||
|
||||
fn on_insert(
|
||||
&self,
|
||||
callback: impl FnMut(&Self::EventContext, &Self::Row) + Send + 'static,
|
||||
) -> DailyTaskLogInsertCallbackId {
|
||||
DailyTaskLogInsertCallbackId(self.imp.on_insert(Box::new(callback)))
|
||||
}
|
||||
|
||||
fn remove_on_insert(&self, callback: DailyTaskLogInsertCallbackId) {
|
||||
self.imp.remove_on_insert(callback.0)
|
||||
}
|
||||
|
||||
type DeleteCallbackId = DailyTaskLogDeleteCallbackId;
|
||||
|
||||
fn on_delete(
|
||||
&self,
|
||||
callback: impl FnMut(&Self::EventContext, &Self::Row) + Send + 'static,
|
||||
) -> DailyTaskLogDeleteCallbackId {
|
||||
DailyTaskLogDeleteCallbackId(self.imp.on_delete(Box::new(callback)))
|
||||
}
|
||||
|
||||
fn remove_on_delete(&self, callback: DailyTaskLogDeleteCallbackId) {
|
||||
self.imp.remove_on_delete(callback.0)
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub(super) fn register_table(client_cache: &mut __sdk::ClientCache<super::RemoteModule>) {
|
||||
let _table = client_cache.get_or_make_table::<DailyTaskLog>("daily_task_log");
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub(super) fn parse_table_update(
|
||||
raw_updates: __ws::v2::TableUpdate,
|
||||
) -> __sdk::Result<__sdk::TableUpdate<DailyTaskLog>> {
|
||||
__sdk::TableUpdate::parse_table_update(raw_updates).map_err(|e| {
|
||||
__sdk::InternalError::failed_parse("TableUpdate<DailyTaskLog>", "TableUpdate")
|
||||
.with_cause(e)
|
||||
.into()
|
||||
})
|
||||
}
|
||||
|
||||
#[allow(non_camel_case_types)]
|
||||
/// Extension trait for query builder access to the table `DailyTaskLog`.
|
||||
///
|
||||
/// Implemented for [`__sdk::QueryTableAccessor`].
|
||||
pub trait daily_task_logQueryTableAccess {
|
||||
#[allow(non_snake_case)]
|
||||
/// Get a query builder for the table `DailyTaskLog`.
|
||||
fn daily_task_log(&self) -> __sdk::__query_builder::Table<DailyTaskLog>;
|
||||
}
|
||||
|
||||
impl daily_task_logQueryTableAccess for __sdk::QueryTableAccessor {
|
||||
fn daily_task_log(&self) -> __sdk::__query_builder::Table<DailyTaskLog> {
|
||||
__sdk::__query_builder::Table::new("daily_task_log")
|
||||
}
|
||||
}
|
||||
48
marts/generated/src/spacetime/daily_task_log_type.rs
Normal file
48
marts/generated/src/spacetime/daily_task_log_type.rs
Normal file
@@ -0,0 +1,48 @@
|
||||
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
#![allow(unused, clippy::all)]
|
||||
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
||||
|
||||
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
|
||||
#[sats(crate = __lib)]
|
||||
pub struct DailyTaskLog {
|
||||
pub daily_task_id: i64,
|
||||
pub done: __sdk::Timestamp,
|
||||
}
|
||||
|
||||
impl __sdk::InModule for DailyTaskLog {
|
||||
type Module = super::RemoteModule;
|
||||
}
|
||||
|
||||
/// Column accessor struct for the table `DailyTaskLog`.
|
||||
///
|
||||
/// Provides typed access to columns for query building.
|
||||
pub struct DailyTaskLogCols {
|
||||
pub daily_task_id: __sdk::__query_builder::Col<DailyTaskLog, i64>,
|
||||
pub done: __sdk::__query_builder::Col<DailyTaskLog, __sdk::Timestamp>,
|
||||
}
|
||||
|
||||
impl __sdk::__query_builder::HasCols for DailyTaskLog {
|
||||
type Cols = DailyTaskLogCols;
|
||||
fn cols(table_name: &'static str) -> Self::Cols {
|
||||
DailyTaskLogCols {
|
||||
daily_task_id: __sdk::__query_builder::Col::new(table_name, "daily_task_id"),
|
||||
done: __sdk::__query_builder::Col::new(table_name, "done"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Indexed column accessor struct for the table `DailyTaskLog`.
|
||||
///
|
||||
/// Provides typed access to indexed columns for query building.
|
||||
pub struct DailyTaskLogIxCols {}
|
||||
|
||||
impl __sdk::__query_builder::HasIxCols for DailyTaskLog {
|
||||
type IxCols = DailyTaskLogIxCols;
|
||||
fn ix_cols(table_name: &'static str) -> Self::IxCols {
|
||||
DailyTaskLogIxCols {}
|
||||
}
|
||||
}
|
||||
|
||||
impl __sdk::__query_builder::CanBeLookupTable for DailyTaskLog {}
|
||||
111
marts/generated/src/spacetime/daily_task_table.rs
Normal file
111
marts/generated/src/spacetime/daily_task_table.rs
Normal file
@@ -0,0 +1,111 @@
|
||||
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
#![allow(unused, clippy::all)]
|
||||
use super::daily_task_type::DailyTask;
|
||||
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
||||
|
||||
/// Table handle for the table `daily_task`.
|
||||
///
|
||||
/// Obtain a handle from the [`DailyTaskTableAccess::daily_task`] method on [`super::RemoteTables`],
|
||||
/// like `ctx.db.daily_task()`.
|
||||
///
|
||||
/// Users are encouraged not to explicitly reference this type,
|
||||
/// but to directly chain method calls,
|
||||
/// like `ctx.db.daily_task().on_insert(...)`.
|
||||
pub struct DailyTaskTableHandle<'ctx> {
|
||||
imp: __sdk::TableHandle<DailyTask>,
|
||||
ctx: std::marker::PhantomData<&'ctx super::RemoteTables>,
|
||||
}
|
||||
|
||||
#[allow(non_camel_case_types)]
|
||||
/// Extension trait for access to the table `daily_task`.
|
||||
///
|
||||
/// Implemented for [`super::RemoteTables`].
|
||||
pub trait DailyTaskTableAccess {
|
||||
#[allow(non_snake_case)]
|
||||
/// Obtain a [`DailyTaskTableHandle`], which mediates access to the table `daily_task`.
|
||||
fn daily_task(&self) -> DailyTaskTableHandle<'_>;
|
||||
}
|
||||
|
||||
impl DailyTaskTableAccess for super::RemoteTables {
|
||||
fn daily_task(&self) -> DailyTaskTableHandle<'_> {
|
||||
DailyTaskTableHandle {
|
||||
imp: self.imp.get_table::<DailyTask>("daily_task"),
|
||||
ctx: std::marker::PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DailyTaskInsertCallbackId(__sdk::CallbackId);
|
||||
pub struct DailyTaskDeleteCallbackId(__sdk::CallbackId);
|
||||
|
||||
impl<'ctx> __sdk::Table for DailyTaskTableHandle<'ctx> {
|
||||
type Row = DailyTask;
|
||||
type EventContext = super::EventContext;
|
||||
|
||||
fn count(&self) -> u64 {
|
||||
self.imp.count()
|
||||
}
|
||||
fn iter(&self) -> impl Iterator<Item = DailyTask> + '_ {
|
||||
self.imp.iter()
|
||||
}
|
||||
|
||||
type InsertCallbackId = DailyTaskInsertCallbackId;
|
||||
|
||||
fn on_insert(
|
||||
&self,
|
||||
callback: impl FnMut(&Self::EventContext, &Self::Row) + Send + 'static,
|
||||
) -> DailyTaskInsertCallbackId {
|
||||
DailyTaskInsertCallbackId(self.imp.on_insert(Box::new(callback)))
|
||||
}
|
||||
|
||||
fn remove_on_insert(&self, callback: DailyTaskInsertCallbackId) {
|
||||
self.imp.remove_on_insert(callback.0)
|
||||
}
|
||||
|
||||
type DeleteCallbackId = DailyTaskDeleteCallbackId;
|
||||
|
||||
fn on_delete(
|
||||
&self,
|
||||
callback: impl FnMut(&Self::EventContext, &Self::Row) + Send + 'static,
|
||||
) -> DailyTaskDeleteCallbackId {
|
||||
DailyTaskDeleteCallbackId(self.imp.on_delete(Box::new(callback)))
|
||||
}
|
||||
|
||||
fn remove_on_delete(&self, callback: DailyTaskDeleteCallbackId) {
|
||||
self.imp.remove_on_delete(callback.0)
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub(super) fn register_table(client_cache: &mut __sdk::ClientCache<super::RemoteModule>) {
|
||||
let _table = client_cache.get_or_make_table::<DailyTask>("daily_task");
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub(super) fn parse_table_update(
|
||||
raw_updates: __ws::v2::TableUpdate,
|
||||
) -> __sdk::Result<__sdk::TableUpdate<DailyTask>> {
|
||||
__sdk::TableUpdate::parse_table_update(raw_updates).map_err(|e| {
|
||||
__sdk::InternalError::failed_parse("TableUpdate<DailyTask>", "TableUpdate")
|
||||
.with_cause(e)
|
||||
.into()
|
||||
})
|
||||
}
|
||||
|
||||
#[allow(non_camel_case_types)]
|
||||
/// Extension trait for query builder access to the table `DailyTask`.
|
||||
///
|
||||
/// Implemented for [`__sdk::QueryTableAccessor`].
|
||||
pub trait daily_taskQueryTableAccess {
|
||||
#[allow(non_snake_case)]
|
||||
/// Get a query builder for the table `DailyTask`.
|
||||
fn daily_task(&self) -> __sdk::__query_builder::Table<DailyTask>;
|
||||
}
|
||||
|
||||
impl daily_taskQueryTableAccess for __sdk::QueryTableAccessor {
|
||||
fn daily_task(&self) -> __sdk::__query_builder::Table<DailyTask> {
|
||||
__sdk::__query_builder::Table::new("daily_task")
|
||||
}
|
||||
}
|
||||
51
marts/generated/src/spacetime/daily_task_type.rs
Normal file
51
marts/generated/src/spacetime/daily_task_type.rs
Normal file
@@ -0,0 +1,51 @@
|
||||
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
#![allow(unused, clippy::all)]
|
||||
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
||||
|
||||
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
|
||||
#[sats(crate = __lib)]
|
||||
pub struct DailyTask {
|
||||
pub id: i64,
|
||||
pub name: String,
|
||||
pub owner: __sdk::Identity,
|
||||
}
|
||||
|
||||
impl __sdk::InModule for DailyTask {
|
||||
type Module = super::RemoteModule;
|
||||
}
|
||||
|
||||
/// Column accessor struct for the table `DailyTask`.
|
||||
///
|
||||
/// Provides typed access to columns for query building.
|
||||
pub struct DailyTaskCols {
|
||||
pub id: __sdk::__query_builder::Col<DailyTask, i64>,
|
||||
pub name: __sdk::__query_builder::Col<DailyTask, String>,
|
||||
pub owner: __sdk::__query_builder::Col<DailyTask, __sdk::Identity>,
|
||||
}
|
||||
|
||||
impl __sdk::__query_builder::HasCols for DailyTask {
|
||||
type Cols = DailyTaskCols;
|
||||
fn cols(table_name: &'static str) -> Self::Cols {
|
||||
DailyTaskCols {
|
||||
id: __sdk::__query_builder::Col::new(table_name, "id"),
|
||||
name: __sdk::__query_builder::Col::new(table_name, "name"),
|
||||
owner: __sdk::__query_builder::Col::new(table_name, "owner"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Indexed column accessor struct for the table `DailyTask`.
|
||||
///
|
||||
/// Provides typed access to indexed columns for query building.
|
||||
pub struct DailyTaskIxCols {}
|
||||
|
||||
impl __sdk::__query_builder::HasIxCols for DailyTask {
|
||||
type IxCols = DailyTaskIxCols;
|
||||
fn ix_cols(table_name: &'static str) -> Self::IxCols {
|
||||
DailyTaskIxCols {}
|
||||
}
|
||||
}
|
||||
|
||||
impl __sdk::__query_builder::CanBeLookupTable for DailyTask {}
|
||||
59
marts/generated/src/spacetime/export_timetrack_procedure.rs
Normal file
59
marts/generated/src/spacetime/export_timetrack_procedure.rs
Normal file
@@ -0,0 +1,59 @@
|
||||
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
#![allow(unused, clippy::all)]
|
||||
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
||||
|
||||
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
|
||||
#[sats(crate = __lib)]
|
||||
struct ExportTimetrackArgs {
|
||||
pub username: String,
|
||||
pub date: String,
|
||||
}
|
||||
|
||||
impl __sdk::InModule for ExportTimetrackArgs {
|
||||
type Module = super::RemoteModule;
|
||||
}
|
||||
|
||||
#[allow(non_camel_case_types)]
|
||||
/// Extension trait for access to the procedure `export_timetrack`.
|
||||
///
|
||||
/// Implemented for [`super::RemoteProcedures`].
|
||||
pub trait export_timetrack {
|
||||
fn export_timetrack(&self, username: String, date: String) {
|
||||
self.export_timetrack_then(username, date, |_, _| {});
|
||||
}
|
||||
|
||||
fn export_timetrack_then(
|
||||
&self,
|
||||
username: String,
|
||||
date: String,
|
||||
|
||||
__callback: impl FnOnce(
|
||||
&super::ProcedureEventContext,
|
||||
Result<Result<String, String>, __sdk::InternalError>,
|
||||
) + Send
|
||||
+ 'static,
|
||||
);
|
||||
}
|
||||
|
||||
impl export_timetrack for super::RemoteProcedures {
|
||||
fn export_timetrack_then(
|
||||
&self,
|
||||
username: String,
|
||||
date: String,
|
||||
|
||||
__callback: impl FnOnce(
|
||||
&super::ProcedureEventContext,
|
||||
Result<Result<String, String>, __sdk::InternalError>,
|
||||
) + Send
|
||||
+ 'static,
|
||||
) {
|
||||
self.imp
|
||||
.invoke_procedure_with_callback::<_, Result<String, String>>(
|
||||
"export_timetrack",
|
||||
ExportTimetrackArgs { username, date },
|
||||
__callback,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
#![allow(unused, clippy::all)]
|
||||
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
||||
|
||||
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
|
||||
#[sats(crate = __lib)]
|
||||
struct FirstTimetrackDateArgs {
|
||||
pub username: String,
|
||||
}
|
||||
|
||||
impl __sdk::InModule for FirstTimetrackDateArgs {
|
||||
type Module = super::RemoteModule;
|
||||
}
|
||||
|
||||
#[allow(non_camel_case_types)]
|
||||
/// Extension trait for access to the procedure `first_timetrack_date`.
|
||||
///
|
||||
/// Implemented for [`super::RemoteProcedures`].
|
||||
pub trait first_timetrack_date {
|
||||
fn first_timetrack_date(&self, username: String) {
|
||||
self.first_timetrack_date_then(username, |_, _| {});
|
||||
}
|
||||
|
||||
fn first_timetrack_date_then(
|
||||
&self,
|
||||
username: String,
|
||||
|
||||
__callback: impl FnOnce(
|
||||
&super::ProcedureEventContext,
|
||||
Result<Result<String, String>, __sdk::InternalError>,
|
||||
) + Send
|
||||
+ 'static,
|
||||
);
|
||||
}
|
||||
|
||||
impl first_timetrack_date for super::RemoteProcedures {
|
||||
fn first_timetrack_date_then(
|
||||
&self,
|
||||
username: String,
|
||||
|
||||
__callback: impl FnOnce(
|
||||
&super::ProcedureEventContext,
|
||||
Result<Result<String, String>, __sdk::InternalError>,
|
||||
) + Send
|
||||
+ 'static,
|
||||
) {
|
||||
self.imp
|
||||
.invoke_procedure_with_callback::<_, Result<String, String>>(
|
||||
"first_timetrack_date",
|
||||
FirstTimetrackDateArgs { username },
|
||||
__callback,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,7 @@ pub struct LogEvent {
|
||||
pub start: __sdk::Timestamp,
|
||||
pub end: __sdk::Timestamp,
|
||||
pub action: Action,
|
||||
pub user: __sdk::Identity,
|
||||
}
|
||||
|
||||
impl __sdk::InModule for LogEvent {
|
||||
@@ -27,6 +28,7 @@ pub struct LogEventCols {
|
||||
pub start: __sdk::__query_builder::Col<LogEvent, __sdk::Timestamp>,
|
||||
pub end: __sdk::__query_builder::Col<LogEvent, __sdk::Timestamp>,
|
||||
pub action: __sdk::__query_builder::Col<LogEvent, Action>,
|
||||
pub user: __sdk::__query_builder::Col<LogEvent, __sdk::Identity>,
|
||||
}
|
||||
|
||||
impl __sdk::__query_builder::HasCols for LogEvent {
|
||||
@@ -37,6 +39,7 @@ impl __sdk::__query_builder::HasCols for LogEvent {
|
||||
start: __sdk::__query_builder::Col::new(table_name, "start"),
|
||||
end: __sdk::__query_builder::Col::new(table_name, "end"),
|
||||
action: __sdk::__query_builder::Col::new(table_name, "action"),
|
||||
user: __sdk::__query_builder::Col::new(table_name, "user"),
|
||||
}
|
||||
}
|
||||
}
|
||||
44
marts/generated/src/spacetime/migrate_procedure.rs
Normal file
44
marts/generated/src/spacetime/migrate_procedure.rs
Normal file
@@ -0,0 +1,44 @@
|
||||
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
#![allow(unused, clippy::all)]
|
||||
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
||||
|
||||
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
|
||||
#[sats(crate = __lib)]
|
||||
struct MigrateArgs {}
|
||||
|
||||
impl __sdk::InModule for MigrateArgs {
|
||||
type Module = super::RemoteModule;
|
||||
}
|
||||
|
||||
#[allow(non_camel_case_types)]
|
||||
/// Extension trait for access to the procedure `migrate`.
|
||||
///
|
||||
/// Implemented for [`super::RemoteProcedures`].
|
||||
pub trait migrate {
|
||||
fn migrate(&self) {
|
||||
self.migrate_then(|_, _| {});
|
||||
}
|
||||
|
||||
fn migrate_then(
|
||||
&self,
|
||||
|
||||
__callback: impl FnOnce(&super::ProcedureEventContext, Result<String, __sdk::InternalError>)
|
||||
+ Send
|
||||
+ 'static,
|
||||
);
|
||||
}
|
||||
|
||||
impl migrate for super::RemoteProcedures {
|
||||
fn migrate_then(
|
||||
&self,
|
||||
|
||||
__callback: impl FnOnce(&super::ProcedureEventContext, Result<String, __sdk::InternalError>)
|
||||
+ Send
|
||||
+ 'static,
|
||||
) {
|
||||
self.imp
|
||||
.invoke_procedure_with_callback::<_, String>("migrate", MigrateArgs {}, __callback);
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
// This was generated using spacetimedb cli version 2.1.0 (commit refs/tags/v2.1.0).
|
||||
// This was generated using spacetimedb cli version 2.6.0 (commit refs/tags/v2.6.0).
|
||||
|
||||
#![allow(unused, clippy::all)]
|
||||
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
||||
@@ -10,8 +10,16 @@ pub mod action_type;
|
||||
pub mod activities_table;
|
||||
pub mod activity_type;
|
||||
pub mod add_activity_reducer;
|
||||
pub mod add_thought_reducer;
|
||||
pub mod daily_task_log_table;
|
||||
pub mod daily_task_log_type;
|
||||
pub mod daily_task_table;
|
||||
pub mod daily_task_type;
|
||||
pub mod export_timetrack_procedure;
|
||||
pub mod first_timetrack_date_procedure;
|
||||
pub mod log_event_type;
|
||||
pub mod log_events_table;
|
||||
pub mod migrate_procedure;
|
||||
pub mod next_action_reducer;
|
||||
pub mod profile_type;
|
||||
pub mod profiles_table;
|
||||
@@ -21,6 +29,9 @@ pub mod task_part_table;
|
||||
pub mod task_part_type;
|
||||
pub mod task_type;
|
||||
pub mod tasks_table;
|
||||
pub mod test_procedure;
|
||||
pub mod thought_type;
|
||||
pub mod thoughts_table;
|
||||
pub mod update_activity_reducer;
|
||||
pub mod update_upcoming_reducer;
|
||||
|
||||
@@ -28,8 +39,16 @@ pub use action_type::Action;
|
||||
pub use activities_table::*;
|
||||
pub use activity_type::Activity;
|
||||
pub use add_activity_reducer::add_activity;
|
||||
pub use add_thought_reducer::add_thought;
|
||||
pub use daily_task_log_table::*;
|
||||
pub use daily_task_log_type::DailyTaskLog;
|
||||
pub use daily_task_table::*;
|
||||
pub use daily_task_type::DailyTask;
|
||||
pub use export_timetrack_procedure::export_timetrack;
|
||||
pub use first_timetrack_date_procedure::first_timetrack_date;
|
||||
pub use log_event_type::LogEvent;
|
||||
pub use log_events_table::*;
|
||||
pub use migrate_procedure::migrate;
|
||||
pub use next_action_reducer::next_action;
|
||||
pub use profile_type::Profile;
|
||||
pub use profiles_table::*;
|
||||
@@ -39,6 +58,9 @@ pub use task_part_table::*;
|
||||
pub use task_part_type::TaskPart;
|
||||
pub use task_type::Task;
|
||||
pub use tasks_table::*;
|
||||
pub use test_procedure::test;
|
||||
pub use thought_type::Thought;
|
||||
pub use thoughts_table::*;
|
||||
pub use update_activity_reducer::update_activity;
|
||||
pub use update_upcoming_reducer::update_upcoming;
|
||||
|
||||
@@ -50,12 +72,29 @@ pub use update_upcoming_reducer::update_upcoming;
|
||||
/// to indicate which reducer caused the event.
|
||||
|
||||
pub enum Reducer {
|
||||
AddActivity { name: String },
|
||||
NextAction,
|
||||
RemoveActivity { activity: Activity },
|
||||
SetActivityName { id: u64, name: String },
|
||||
UpdateActivity { activity: Activity },
|
||||
UpdateUpcoming { upcoming: Vec<Action> },
|
||||
AddActivity {
|
||||
name: String,
|
||||
},
|
||||
AddThought {
|
||||
thought: String,
|
||||
},
|
||||
NextAction {
|
||||
break_flag: bool,
|
||||
action: Option<Action>,
|
||||
},
|
||||
RemoveActivity {
|
||||
activity: Activity,
|
||||
},
|
||||
SetActivityName {
|
||||
id: u64,
|
||||
name: String,
|
||||
},
|
||||
UpdateActivity {
|
||||
activity: Activity,
|
||||
},
|
||||
UpdateUpcoming {
|
||||
upcoming: Vec<Action>,
|
||||
},
|
||||
}
|
||||
|
||||
impl __sdk::InModule for Reducer {
|
||||
@@ -66,7 +105,8 @@ impl __sdk::Reducer for Reducer {
|
||||
fn reducer_name(&self) -> &'static str {
|
||||
match self {
|
||||
Reducer::AddActivity { .. } => "add_activity",
|
||||
Reducer::NextAction => "next_action",
|
||||
Reducer::AddThought { .. } => "add_thought",
|
||||
Reducer::NextAction { .. } => "next_action",
|
||||
Reducer::RemoveActivity { .. } => "remove_activity",
|
||||
Reducer::SetActivityName { .. } => "set_activity_name",
|
||||
Reducer::UpdateActivity { .. } => "update_activity",
|
||||
@@ -80,7 +120,17 @@ impl __sdk::Reducer for Reducer {
|
||||
Reducer::AddActivity { name } => {
|
||||
__sats::bsatn::to_vec(&add_activity_reducer::AddActivityArgs { name: name.clone() })
|
||||
}
|
||||
Reducer::NextAction => __sats::bsatn::to_vec(&next_action_reducer::NextActionArgs {}),
|
||||
Reducer::AddThought { thought } => {
|
||||
__sats::bsatn::to_vec(&add_thought_reducer::AddThoughtArgs {
|
||||
thought: thought.clone(),
|
||||
})
|
||||
}
|
||||
Reducer::NextAction { break_flag, action } => {
|
||||
__sats::bsatn::to_vec(&next_action_reducer::NextActionArgs {
|
||||
break_flag: break_flag.clone(),
|
||||
action: action.clone(),
|
||||
})
|
||||
}
|
||||
Reducer::RemoveActivity { activity } => {
|
||||
__sats::bsatn::to_vec(&remove_activity_reducer::RemoveActivityArgs {
|
||||
activity: activity.clone(),
|
||||
@@ -112,10 +162,13 @@ impl __sdk::Reducer for Reducer {
|
||||
#[doc(hidden)]
|
||||
pub struct DbUpdate {
|
||||
activities: __sdk::TableUpdate<Activity>,
|
||||
daily_task: __sdk::TableUpdate<DailyTask>,
|
||||
daily_task_log: __sdk::TableUpdate<DailyTaskLog>,
|
||||
log_events: __sdk::TableUpdate<LogEvent>,
|
||||
profiles: __sdk::TableUpdate<Profile>,
|
||||
task_part: __sdk::TableUpdate<TaskPart>,
|
||||
tasks: __sdk::TableUpdate<Task>,
|
||||
thoughts: __sdk::TableUpdate<Thought>,
|
||||
}
|
||||
|
||||
impl TryFrom<__ws::v2::TransactionUpdate> for DbUpdate {
|
||||
@@ -127,6 +180,12 @@ impl TryFrom<__ws::v2::TransactionUpdate> for DbUpdate {
|
||||
"activities" => db_update
|
||||
.activities
|
||||
.append(activities_table::parse_table_update(table_update)?),
|
||||
"daily_task" => db_update
|
||||
.daily_task
|
||||
.append(daily_task_table::parse_table_update(table_update)?),
|
||||
"daily_task_log" => db_update
|
||||
.daily_task_log
|
||||
.append(daily_task_log_table::parse_table_update(table_update)?),
|
||||
"log_events" => db_update
|
||||
.log_events
|
||||
.append(log_events_table::parse_table_update(table_update)?),
|
||||
@@ -139,6 +198,9 @@ impl TryFrom<__ws::v2::TransactionUpdate> for DbUpdate {
|
||||
"tasks" => db_update
|
||||
.tasks
|
||||
.append(tasks_table::parse_table_update(table_update)?),
|
||||
"thoughts" => db_update
|
||||
.thoughts
|
||||
.append(thoughts_table::parse_table_update(table_update)?),
|
||||
|
||||
unknown => {
|
||||
return Err(__sdk::InternalError::unknown_name(
|
||||
@@ -168,6 +230,9 @@ impl __sdk::DbUpdate for DbUpdate {
|
||||
diff.activities = cache
|
||||
.apply_diff_to_table::<Activity>("activities", &self.activities)
|
||||
.with_updates_by_pk(|row| &row.id);
|
||||
diff.daily_task = cache.apply_diff_to_table::<DailyTask>("daily_task", &self.daily_task);
|
||||
diff.daily_task_log =
|
||||
cache.apply_diff_to_table::<DailyTaskLog>("daily_task_log", &self.daily_task_log);
|
||||
diff.log_events = cache
|
||||
.apply_diff_to_table::<LogEvent>("log_events", &self.log_events)
|
||||
.with_updates_by_pk(|row| &row.id);
|
||||
@@ -180,6 +245,9 @@ impl __sdk::DbUpdate for DbUpdate {
|
||||
diff.tasks = cache
|
||||
.apply_diff_to_table::<Task>("tasks", &self.tasks)
|
||||
.with_updates_by_pk(|row| &row.id);
|
||||
diff.thoughts = cache
|
||||
.apply_diff_to_table::<Thought>("thoughts", &self.thoughts)
|
||||
.with_updates_by_pk(|row| &row.id);
|
||||
|
||||
diff
|
||||
}
|
||||
@@ -190,6 +258,12 @@ impl __sdk::DbUpdate for DbUpdate {
|
||||
"activities" => db_update
|
||||
.activities
|
||||
.append(__sdk::parse_row_list_as_inserts(table_rows.rows)?),
|
||||
"daily_task" => db_update
|
||||
.daily_task
|
||||
.append(__sdk::parse_row_list_as_inserts(table_rows.rows)?),
|
||||
"daily_task_log" => db_update
|
||||
.daily_task_log
|
||||
.append(__sdk::parse_row_list_as_inserts(table_rows.rows)?),
|
||||
"log_events" => db_update
|
||||
.log_events
|
||||
.append(__sdk::parse_row_list_as_inserts(table_rows.rows)?),
|
||||
@@ -202,6 +276,9 @@ impl __sdk::DbUpdate for DbUpdate {
|
||||
"tasks" => db_update
|
||||
.tasks
|
||||
.append(__sdk::parse_row_list_as_inserts(table_rows.rows)?),
|
||||
"thoughts" => db_update
|
||||
.thoughts
|
||||
.append(__sdk::parse_row_list_as_inserts(table_rows.rows)?),
|
||||
unknown => {
|
||||
return Err(
|
||||
__sdk::InternalError::unknown_name("table", unknown, "QueryRows").into(),
|
||||
@@ -218,6 +295,12 @@ impl __sdk::DbUpdate for DbUpdate {
|
||||
"activities" => db_update
|
||||
.activities
|
||||
.append(__sdk::parse_row_list_as_deletes(table_rows.rows)?),
|
||||
"daily_task" => db_update
|
||||
.daily_task
|
||||
.append(__sdk::parse_row_list_as_deletes(table_rows.rows)?),
|
||||
"daily_task_log" => db_update
|
||||
.daily_task_log
|
||||
.append(__sdk::parse_row_list_as_deletes(table_rows.rows)?),
|
||||
"log_events" => db_update
|
||||
.log_events
|
||||
.append(__sdk::parse_row_list_as_deletes(table_rows.rows)?),
|
||||
@@ -230,6 +313,9 @@ impl __sdk::DbUpdate for DbUpdate {
|
||||
"tasks" => db_update
|
||||
.tasks
|
||||
.append(__sdk::parse_row_list_as_deletes(table_rows.rows)?),
|
||||
"thoughts" => db_update
|
||||
.thoughts
|
||||
.append(__sdk::parse_row_list_as_deletes(table_rows.rows)?),
|
||||
unknown => {
|
||||
return Err(
|
||||
__sdk::InternalError::unknown_name("table", unknown, "QueryRows").into(),
|
||||
@@ -246,10 +332,13 @@ impl __sdk::DbUpdate for DbUpdate {
|
||||
#[doc(hidden)]
|
||||
pub struct AppliedDiff<'r> {
|
||||
activities: __sdk::TableAppliedDiff<'r, Activity>,
|
||||
daily_task: __sdk::TableAppliedDiff<'r, DailyTask>,
|
||||
daily_task_log: __sdk::TableAppliedDiff<'r, DailyTaskLog>,
|
||||
log_events: __sdk::TableAppliedDiff<'r, LogEvent>,
|
||||
profiles: __sdk::TableAppliedDiff<'r, Profile>,
|
||||
task_part: __sdk::TableAppliedDiff<'r, TaskPart>,
|
||||
tasks: __sdk::TableAppliedDiff<'r, Task>,
|
||||
thoughts: __sdk::TableAppliedDiff<'r, Thought>,
|
||||
__unused: std::marker::PhantomData<&'r ()>,
|
||||
}
|
||||
|
||||
@@ -264,10 +353,17 @@ impl<'r> __sdk::AppliedDiff<'r> for AppliedDiff<'r> {
|
||||
callbacks: &mut __sdk::DbCallbacks<RemoteModule>,
|
||||
) {
|
||||
callbacks.invoke_table_row_callbacks::<Activity>("activities", &self.activities, event);
|
||||
callbacks.invoke_table_row_callbacks::<DailyTask>("daily_task", &self.daily_task, event);
|
||||
callbacks.invoke_table_row_callbacks::<DailyTaskLog>(
|
||||
"daily_task_log",
|
||||
&self.daily_task_log,
|
||||
event,
|
||||
);
|
||||
callbacks.invoke_table_row_callbacks::<LogEvent>("log_events", &self.log_events, event);
|
||||
callbacks.invoke_table_row_callbacks::<Profile>("profiles", &self.profiles, event);
|
||||
callbacks.invoke_table_row_callbacks::<TaskPart>("task_part", &self.task_part, event);
|
||||
callbacks.invoke_table_row_callbacks::<Task>("tasks", &self.tasks, event);
|
||||
callbacks.invoke_table_row_callbacks::<Thought>("thoughts", &self.thoughts, event);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -929,11 +1025,22 @@ impl __sdk::SpacetimeModule for RemoteModule {
|
||||
|
||||
fn register_tables(client_cache: &mut __sdk::ClientCache<Self>) {
|
||||
activities_table::register_table(client_cache);
|
||||
daily_task_table::register_table(client_cache);
|
||||
daily_task_log_table::register_table(client_cache);
|
||||
log_events_table::register_table(client_cache);
|
||||
profiles_table::register_table(client_cache);
|
||||
task_part_table::register_table(client_cache);
|
||||
tasks_table::register_table(client_cache);
|
||||
thoughts_table::register_table(client_cache);
|
||||
}
|
||||
const ALL_TABLE_NAMES: &'static [&'static str] =
|
||||
&["activities", "log_events", "profiles", "task_part", "tasks"];
|
||||
const ALL_TABLE_NAMES: &'static [&'static str] = &[
|
||||
"activities",
|
||||
"daily_task",
|
||||
"daily_task_log",
|
||||
"log_events",
|
||||
"profiles",
|
||||
"task_part",
|
||||
"tasks",
|
||||
"thoughts",
|
||||
];
|
||||
}
|
||||
@@ -4,13 +4,21 @@
|
||||
#![allow(unused, clippy::all)]
|
||||
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
||||
|
||||
use super::action_type::Action;
|
||||
|
||||
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
|
||||
#[sats(crate = __lib)]
|
||||
pub(super) struct NextActionArgs {}
|
||||
pub(super) struct NextActionArgs {
|
||||
pub break_flag: bool,
|
||||
pub action: Option<Action>,
|
||||
}
|
||||
|
||||
impl From<NextActionArgs> for super::Reducer {
|
||||
fn from(args: NextActionArgs) -> Self {
|
||||
Self::NextAction
|
||||
Self::NextAction {
|
||||
break_flag: args.break_flag,
|
||||
action: args.action,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,8 +37,8 @@ pub trait next_action {
|
||||
/// The reducer will run asynchronously in the future,
|
||||
/// and this method provides no way to listen for its completion status.
|
||||
/// /// Use [`next_action:next_action_then`] to run a callback after the reducer completes.
|
||||
fn next_action(&self) -> __sdk::Result<()> {
|
||||
self.next_action_then(|_, _| {})
|
||||
fn next_action(&self, break_flag: bool, action: Option<Action>) -> __sdk::Result<()> {
|
||||
self.next_action_then(break_flag, action, |_, _| {})
|
||||
}
|
||||
|
||||
/// Request that the remote module invoke the reducer `next_action` to run as soon as possible,
|
||||
@@ -41,6 +49,8 @@ pub trait next_action {
|
||||
/// and its status can be observed with the `callback`.
|
||||
fn next_action_then(
|
||||
&self,
|
||||
break_flag: bool,
|
||||
action: Option<Action>,
|
||||
|
||||
callback: impl FnOnce(&super::ReducerEventContext, Result<Result<(), String>, __sdk::InternalError>)
|
||||
+ Send
|
||||
@@ -51,12 +61,14 @@ pub trait next_action {
|
||||
impl next_action for super::RemoteReducers {
|
||||
fn next_action_then(
|
||||
&self,
|
||||
break_flag: bool,
|
||||
action: Option<Action>,
|
||||
|
||||
callback: impl FnOnce(&super::ReducerEventContext, Result<Result<(), String>, __sdk::InternalError>)
|
||||
+ Send
|
||||
+ 'static,
|
||||
) -> __sdk::Result<()> {
|
||||
self.imp
|
||||
.invoke_reducer_with_callback(NextActionArgs {}, callback)
|
||||
.invoke_reducer_with_callback(NextActionArgs { break_flag, action }, callback)
|
||||
}
|
||||
}
|
||||
44
marts/generated/src/spacetime/test_procedure.rs
Normal file
44
marts/generated/src/spacetime/test_procedure.rs
Normal file
@@ -0,0 +1,44 @@
|
||||
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
#![allow(unused, clippy::all)]
|
||||
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
||||
|
||||
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
|
||||
#[sats(crate = __lib)]
|
||||
struct TestArgs {}
|
||||
|
||||
impl __sdk::InModule for TestArgs {
|
||||
type Module = super::RemoteModule;
|
||||
}
|
||||
|
||||
#[allow(non_camel_case_types)]
|
||||
/// Extension trait for access to the procedure `test`.
|
||||
///
|
||||
/// Implemented for [`super::RemoteProcedures`].
|
||||
pub trait test {
|
||||
fn test(&self) {
|
||||
self.test_then(|_, _| {});
|
||||
}
|
||||
|
||||
fn test_then(
|
||||
&self,
|
||||
|
||||
__callback: impl FnOnce(&super::ProcedureEventContext, Result<String, __sdk::InternalError>)
|
||||
+ Send
|
||||
+ 'static,
|
||||
);
|
||||
}
|
||||
|
||||
impl test for super::RemoteProcedures {
|
||||
fn test_then(
|
||||
&self,
|
||||
|
||||
__callback: impl FnOnce(&super::ProcedureEventContext, Result<String, __sdk::InternalError>)
|
||||
+ Send
|
||||
+ 'static,
|
||||
) {
|
||||
self.imp
|
||||
.invoke_procedure_with_callback::<_, String>("test", TestArgs {}, __callback);
|
||||
}
|
||||
}
|
||||
60
marts/generated/src/spacetime/thought_type.rs
Normal file
60
marts/generated/src/spacetime/thought_type.rs
Normal file
@@ -0,0 +1,60 @@
|
||||
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
#![allow(unused, clippy::all)]
|
||||
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
||||
|
||||
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
|
||||
#[sats(crate = __lib)]
|
||||
pub struct Thought {
|
||||
pub id: u64,
|
||||
pub text: String,
|
||||
pub user: __sdk::Identity,
|
||||
pub time: __sdk::Timestamp,
|
||||
}
|
||||
|
||||
impl __sdk::InModule for Thought {
|
||||
type Module = super::RemoteModule;
|
||||
}
|
||||
|
||||
/// Column accessor struct for the table `Thought`.
|
||||
///
|
||||
/// Provides typed access to columns for query building.
|
||||
pub struct ThoughtCols {
|
||||
pub id: __sdk::__query_builder::Col<Thought, u64>,
|
||||
pub text: __sdk::__query_builder::Col<Thought, String>,
|
||||
pub user: __sdk::__query_builder::Col<Thought, __sdk::Identity>,
|
||||
pub time: __sdk::__query_builder::Col<Thought, __sdk::Timestamp>,
|
||||
}
|
||||
|
||||
impl __sdk::__query_builder::HasCols for Thought {
|
||||
type Cols = ThoughtCols;
|
||||
fn cols(table_name: &'static str) -> Self::Cols {
|
||||
ThoughtCols {
|
||||
id: __sdk::__query_builder::Col::new(table_name, "id"),
|
||||
text: __sdk::__query_builder::Col::new(table_name, "text"),
|
||||
user: __sdk::__query_builder::Col::new(table_name, "user"),
|
||||
time: __sdk::__query_builder::Col::new(table_name, "time"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Indexed column accessor struct for the table `Thought`.
|
||||
///
|
||||
/// Provides typed access to indexed columns for query building.
|
||||
pub struct ThoughtIxCols {
|
||||
pub id: __sdk::__query_builder::IxCol<Thought, u64>,
|
||||
pub time: __sdk::__query_builder::IxCol<Thought, __sdk::Timestamp>,
|
||||
}
|
||||
|
||||
impl __sdk::__query_builder::HasIxCols for Thought {
|
||||
type IxCols = ThoughtIxCols;
|
||||
fn ix_cols(table_name: &'static str) -> Self::IxCols {
|
||||
ThoughtIxCols {
|
||||
id: __sdk::__query_builder::IxCol::new(table_name, "id"),
|
||||
time: __sdk::__query_builder::IxCol::new(table_name, "time"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl __sdk::__query_builder::CanBeLookupTable for Thought {}
|
||||
159
marts/generated/src/spacetime/thoughts_table.rs
Normal file
159
marts/generated/src/spacetime/thoughts_table.rs
Normal file
@@ -0,0 +1,159 @@
|
||||
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
#![allow(unused, clippy::all)]
|
||||
use super::thought_type::Thought;
|
||||
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
||||
|
||||
/// Table handle for the table `thoughts`.
|
||||
///
|
||||
/// Obtain a handle from the [`ThoughtsTableAccess::thoughts`] method on [`super::RemoteTables`],
|
||||
/// like `ctx.db.thoughts()`.
|
||||
///
|
||||
/// Users are encouraged not to explicitly reference this type,
|
||||
/// but to directly chain method calls,
|
||||
/// like `ctx.db.thoughts().on_insert(...)`.
|
||||
pub struct ThoughtsTableHandle<'ctx> {
|
||||
imp: __sdk::TableHandle<Thought>,
|
||||
ctx: std::marker::PhantomData<&'ctx super::RemoteTables>,
|
||||
}
|
||||
|
||||
#[allow(non_camel_case_types)]
|
||||
/// Extension trait for access to the table `thoughts`.
|
||||
///
|
||||
/// Implemented for [`super::RemoteTables`].
|
||||
pub trait ThoughtsTableAccess {
|
||||
#[allow(non_snake_case)]
|
||||
/// Obtain a [`ThoughtsTableHandle`], which mediates access to the table `thoughts`.
|
||||
fn thoughts(&self) -> ThoughtsTableHandle<'_>;
|
||||
}
|
||||
|
||||
impl ThoughtsTableAccess for super::RemoteTables {
|
||||
fn thoughts(&self) -> ThoughtsTableHandle<'_> {
|
||||
ThoughtsTableHandle {
|
||||
imp: self.imp.get_table::<Thought>("thoughts"),
|
||||
ctx: std::marker::PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ThoughtsInsertCallbackId(__sdk::CallbackId);
|
||||
pub struct ThoughtsDeleteCallbackId(__sdk::CallbackId);
|
||||
|
||||
impl<'ctx> __sdk::Table for ThoughtsTableHandle<'ctx> {
|
||||
type Row = Thought;
|
||||
type EventContext = super::EventContext;
|
||||
|
||||
fn count(&self) -> u64 {
|
||||
self.imp.count()
|
||||
}
|
||||
fn iter(&self) -> impl Iterator<Item = Thought> + '_ {
|
||||
self.imp.iter()
|
||||
}
|
||||
|
||||
type InsertCallbackId = ThoughtsInsertCallbackId;
|
||||
|
||||
fn on_insert(
|
||||
&self,
|
||||
callback: impl FnMut(&Self::EventContext, &Self::Row) + Send + 'static,
|
||||
) -> ThoughtsInsertCallbackId {
|
||||
ThoughtsInsertCallbackId(self.imp.on_insert(Box::new(callback)))
|
||||
}
|
||||
|
||||
fn remove_on_insert(&self, callback: ThoughtsInsertCallbackId) {
|
||||
self.imp.remove_on_insert(callback.0)
|
||||
}
|
||||
|
||||
type DeleteCallbackId = ThoughtsDeleteCallbackId;
|
||||
|
||||
fn on_delete(
|
||||
&self,
|
||||
callback: impl FnMut(&Self::EventContext, &Self::Row) + Send + 'static,
|
||||
) -> ThoughtsDeleteCallbackId {
|
||||
ThoughtsDeleteCallbackId(self.imp.on_delete(Box::new(callback)))
|
||||
}
|
||||
|
||||
fn remove_on_delete(&self, callback: ThoughtsDeleteCallbackId) {
|
||||
self.imp.remove_on_delete(callback.0)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ThoughtsUpdateCallbackId(__sdk::CallbackId);
|
||||
|
||||
impl<'ctx> __sdk::TableWithPrimaryKey for ThoughtsTableHandle<'ctx> {
|
||||
type UpdateCallbackId = ThoughtsUpdateCallbackId;
|
||||
|
||||
fn on_update(
|
||||
&self,
|
||||
callback: impl FnMut(&Self::EventContext, &Self::Row, &Self::Row) + Send + 'static,
|
||||
) -> ThoughtsUpdateCallbackId {
|
||||
ThoughtsUpdateCallbackId(self.imp.on_update(Box::new(callback)))
|
||||
}
|
||||
|
||||
fn remove_on_update(&self, callback: ThoughtsUpdateCallbackId) {
|
||||
self.imp.remove_on_update(callback.0)
|
||||
}
|
||||
}
|
||||
|
||||
/// Access to the `id` unique index on the table `thoughts`,
|
||||
/// which allows point queries on the field of the same name
|
||||
/// via the [`ThoughtsIdUnique::find`] method.
|
||||
///
|
||||
/// Users are encouraged not to explicitly reference this type,
|
||||
/// but to directly chain method calls,
|
||||
/// like `ctx.db.thoughts().id().find(...)`.
|
||||
pub struct ThoughtsIdUnique<'ctx> {
|
||||
imp: __sdk::UniqueConstraintHandle<Thought, u64>,
|
||||
phantom: std::marker::PhantomData<&'ctx super::RemoteTables>,
|
||||
}
|
||||
|
||||
impl<'ctx> ThoughtsTableHandle<'ctx> {
|
||||
/// Get a handle on the `id` unique index on the table `thoughts`.
|
||||
pub fn id(&self) -> ThoughtsIdUnique<'ctx> {
|
||||
ThoughtsIdUnique {
|
||||
imp: self.imp.get_unique_constraint::<u64>("id"),
|
||||
phantom: std::marker::PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ctx> ThoughtsIdUnique<'ctx> {
|
||||
/// Find the subscribed row whose `id` column value is equal to `col_val`,
|
||||
/// if such a row is present in the client cache.
|
||||
pub fn find(&self, col_val: &u64) -> Option<Thought> {
|
||||
self.imp.find(col_val)
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub(super) fn register_table(client_cache: &mut __sdk::ClientCache<super::RemoteModule>) {
|
||||
let _table = client_cache.get_or_make_table::<Thought>("thoughts");
|
||||
_table.add_unique_constraint::<u64>("id", |row| &row.id);
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub(super) fn parse_table_update(
|
||||
raw_updates: __ws::v2::TableUpdate,
|
||||
) -> __sdk::Result<__sdk::TableUpdate<Thought>> {
|
||||
__sdk::TableUpdate::parse_table_update(raw_updates).map_err(|e| {
|
||||
__sdk::InternalError::failed_parse("TableUpdate<Thought>", "TableUpdate")
|
||||
.with_cause(e)
|
||||
.into()
|
||||
})
|
||||
}
|
||||
|
||||
#[allow(non_camel_case_types)]
|
||||
/// Extension trait for query builder access to the table `Thought`.
|
||||
///
|
||||
/// Implemented for [`__sdk::QueryTableAccessor`].
|
||||
pub trait thoughtsQueryTableAccess {
|
||||
#[allow(non_snake_case)]
|
||||
/// Get a query builder for the table `Thought`.
|
||||
fn thoughts(&self) -> __sdk::__query_builder::Table<Thought>;
|
||||
}
|
||||
|
||||
impl thoughtsQueryTableAccess for __sdk::QueryTableAccessor {
|
||||
fn thoughts(&self) -> __sdk::__query_builder::Table<Thought> {
|
||||
__sdk::__query_builder::Table::new("thoughts")
|
||||
}
|
||||
}
|
||||
@@ -9,5 +9,8 @@ edition = "2021"
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[dependencies]
|
||||
spacetimedb = "2.1.0"
|
||||
spacetimedb = { version = "2.4.0", features = ["unstable"] }
|
||||
log = "0.4"
|
||||
chrono = { version = "0.4.45", features = [], default-features = false }
|
||||
chrono-tz = { version = "0.10.4", default-features = false }
|
||||
url = "2.5.8"
|
||||
393
marts/spacetime/src/lib.rs
Normal file
393
marts/spacetime/src/lib.rs
Normal file
@@ -0,0 +1,393 @@
|
||||
use std::collections::HashMap;
|
||||
use std::time::Duration;
|
||||
|
||||
use chrono::{DateTime, TimeZone};
|
||||
use chrono_tz::Europe;
|
||||
use log::*;
|
||||
use spacetimedb::SpacetimeType;
|
||||
use spacetimedb::sys::raw::index_id_from_name;
|
||||
use spacetimedb::{Identity, ReducerContext, ProcedureContext, Table, Timestamp, reducer, table, procedure};
|
||||
use spacetimedb::http::*;
|
||||
|
||||
#[table(accessor = profiles, public)]
|
||||
pub struct Profile {
|
||||
#[primary_key]
|
||||
identity: Identity,
|
||||
#[unique]
|
||||
username: String,
|
||||
}
|
||||
|
||||
#[table(accessor = task_part, public)]
|
||||
pub struct TaskPart {
|
||||
#[primary_key]
|
||||
identity: Identity,
|
||||
start_of_current: Timestamp,
|
||||
current: Action,
|
||||
upcoming: Vec<Action>,
|
||||
}
|
||||
|
||||
#[table(accessor = activities, public)]
|
||||
pub struct Activity {
|
||||
#[primary_key]
|
||||
#[auto_inc]
|
||||
id: u64,
|
||||
name: String,
|
||||
task: Option<u64>,
|
||||
parents_ids: Vec<u64>,
|
||||
}
|
||||
|
||||
#[table(accessor = tasks, public)]
|
||||
pub struct Task {
|
||||
#[primary_key]
|
||||
#[auto_inc]
|
||||
id: u64,
|
||||
done: bool,
|
||||
name: String,
|
||||
parent: Option<u64>,
|
||||
}
|
||||
|
||||
#[table(accessor = thoughts, public)]
|
||||
pub struct Thought {
|
||||
#[primary_key]
|
||||
#[auto_inc]
|
||||
id: u64,
|
||||
text: String,
|
||||
user: Identity,
|
||||
#[index(btree)]
|
||||
time: Timestamp,
|
||||
}
|
||||
|
||||
#[table(accessor = action)]
|
||||
#[derive(Clone)]
|
||||
pub struct Action {
|
||||
name: String,
|
||||
activity: Option<u64>,
|
||||
task: Option<u64>,
|
||||
start: Option<Timestamp>,
|
||||
end: Option<Timestamp>,
|
||||
duration: Option<u64>,
|
||||
}
|
||||
|
||||
impl Action {
|
||||
pub fn name_only(name: String) -> Self {
|
||||
Action {
|
||||
name,
|
||||
activity: None,
|
||||
task: None,
|
||||
start: None,
|
||||
end: None,
|
||||
duration: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[reducer]
|
||||
pub fn add_thought(ctx: &ReducerContext, thought: String) -> Result<(), String> {
|
||||
ctx.db.thoughts().try_insert(Thought {
|
||||
id: 0,
|
||||
text: thought,
|
||||
user: ctx.sender(),
|
||||
time: ctx.timestamp,
|
||||
})?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[table(accessor = log_events, public, index(accessor = log_events_index, btree(columns = [user, end])))]
|
||||
pub struct LogEvent {
|
||||
#[primary_key]
|
||||
#[auto_inc]
|
||||
id: u64,
|
||||
start: Timestamp,
|
||||
end: Timestamp,
|
||||
action: Action,
|
||||
user: Identity,
|
||||
}
|
||||
|
||||
#[reducer(init)]
|
||||
pub fn init(_ctx: &ReducerContext) {
|
||||
// Called when the module is initially published
|
||||
info!("hiiiiiii from ppc module");
|
||||
}
|
||||
|
||||
// the one from ppc, hardcoded rn, as we for now only support config at run time.
|
||||
// this needs to be loaded at build time. (this code runs as a wasm module on the spacetimedb server)
|
||||
const PPC_AUTH_CLIENT_ID: &str = "363331597203734531";
|
||||
|
||||
#[reducer(client_connected)]
|
||||
pub fn connect(ctx: &ReducerContext) -> Result<(), String> {
|
||||
check_auth(ctx)?;
|
||||
info!("hooooo");
|
||||
|
||||
// add user
|
||||
let _ = ctx.db.profiles().try_insert(Profile {
|
||||
identity: ctx.sender(),
|
||||
username: ctx.sender().to_string(),
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn check_auth(ctx: &ReducerContext) -> Result<(), String> {
|
||||
let jwt = ctx
|
||||
.sender_auth()
|
||||
.jwt()
|
||||
.ok_or("Authentication required".to_string())?;
|
||||
if jwt.issuer() != "https://auth.ppc.social" {
|
||||
return Err("Invalid issuer".to_string());
|
||||
}
|
||||
|
||||
if !jwt.audience().iter().any(|a| a == PPC_AUTH_CLIENT_ID) {
|
||||
return Err("Invalid audience".to_string());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[reducer(client_disconnected)]
|
||||
pub fn identity_disconnected(_ctx: &ReducerContext) {
|
||||
// Called everytime a client disconnects
|
||||
}
|
||||
|
||||
#[reducer]
|
||||
pub fn add_activity(ctx: &ReducerContext, name: String) {
|
||||
ctx.db.activities().insert(Activity {
|
||||
name,
|
||||
task: None,
|
||||
id: 0, // auto-increments
|
||||
parents_ids: Vec::new(),
|
||||
});
|
||||
}
|
||||
|
||||
#[reducer]
|
||||
pub fn set_activity_name(ctx: &ReducerContext, id: u64, name: String) {
|
||||
if let Some(old_ac) = ctx.db.activities().id().find(id) {
|
||||
ctx.db.activities().id().update(Activity { name, ..old_ac });
|
||||
} else {
|
||||
return ();
|
||||
}
|
||||
}
|
||||
|
||||
#[reducer]
|
||||
pub fn update_upcoming(ctx: &ReducerContext, upcoming: Vec<Action>) {
|
||||
if ctx.db.task_part().identity().find(ctx.sender()).is_none() {
|
||||
ctx.db.task_part().insert(TaskPart {
|
||||
upcoming,
|
||||
identity: ctx.sender(),
|
||||
current: Action {
|
||||
name: "filler".to_string(),
|
||||
activity: None,
|
||||
task: None,
|
||||
start: None,
|
||||
end: None,
|
||||
duration: None,
|
||||
},
|
||||
start_of_current: ctx.timestamp,
|
||||
});
|
||||
} else {
|
||||
ctx.db.task_part().identity().update(TaskPart {
|
||||
upcoming,
|
||||
..ctx.db.task_part().identity().find(&ctx.sender()).unwrap()
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#[reducer]
|
||||
pub fn next_action(ctx: &ReducerContext, break_flag: bool, action: Option<Action>) -> Result<(), String> {
|
||||
if let Some(mut task_part) = ctx.db.task_part().identity().find(&ctx.sender()) {
|
||||
if task_part.upcoming.is_empty() {
|
||||
return Err("No upcoming actions".to_string());
|
||||
}
|
||||
let new_action = match action {
|
||||
Some(action) => action,
|
||||
// when we do "ppc nextAction -b" ... we want to do a generic break "b" action.
|
||||
None => if break_flag { Action::name_only("idk".to_string()) } else { task_part.upcoming.remove(0) },
|
||||
};
|
||||
// when break flag, push current_action again to the front of upcoming
|
||||
if break_flag {
|
||||
// insert generic "b" action when no one is specified
|
||||
task_part.upcoming.insert(0, task_part.current.clone());
|
||||
}
|
||||
let start_of_old_action = task_part.start_of_current;
|
||||
ctx.db.task_part().identity().update(TaskPart {
|
||||
upcoming: task_part.upcoming,
|
||||
identity: task_part.identity,
|
||||
current: new_action.clone(),
|
||||
start_of_current: ctx.timestamp,
|
||||
});
|
||||
|
||||
// create log event
|
||||
ctx.db.log_events().insert(LogEvent {
|
||||
start: start_of_old_action,
|
||||
end: ctx.timestamp,
|
||||
action: task_part.current,
|
||||
id: 0,
|
||||
user: ctx.sender(),
|
||||
});
|
||||
Ok(())
|
||||
} else {
|
||||
Err("Task part data not found for this user".to_string())
|
||||
}
|
||||
}
|
||||
|
||||
#[reducer]
|
||||
pub fn update_activity(ctx: &ReducerContext, activity: Activity) {
|
||||
if activity.id == 0 {
|
||||
ctx.db.activities().insert(activity);
|
||||
} else {
|
||||
ctx.db.activities().id().update(activity);
|
||||
}
|
||||
}
|
||||
|
||||
#[reducer]
|
||||
pub fn remove_activity(ctx: &ReducerContext, activity: Activity) {
|
||||
ctx.db.activities().delete(activity);
|
||||
}
|
||||
|
||||
|
||||
//////////////////// daily metric ///////////////////
|
||||
|
||||
#[table(accessor = daily_task, public)]
|
||||
pub struct DailyTask {
|
||||
id: i64,
|
||||
name: String,
|
||||
owner: Identity,
|
||||
}
|
||||
|
||||
#[table(accessor = daily_task_log, public)]
|
||||
pub struct DailyTaskLog {
|
||||
daily_task_id: i64,
|
||||
done: Timestamp,
|
||||
}
|
||||
|
||||
#[procedure]
|
||||
fn first_timetrack_date(ctx: &mut ProcedureContext, username: String) -> Result<String, String> {
|
||||
let timestamp = ctx.try_with_tx(|tx| {
|
||||
let profile = tx.db.profiles().username().find(username.clone()).ok_or("No such Profile".to_string())?;
|
||||
let event = tx.db.log_events()
|
||||
.log_events_index()
|
||||
.filter(&profile.identity)
|
||||
.nth(0)
|
||||
.ok_or("Profile has no row in log_events")?;
|
||||
let time = event.start
|
||||
.to_chrono_date_time().map_err(|e| format!("chrono error: {e}"))?
|
||||
.with_timezone(&chrono_tz::Europe::Vienna);
|
||||
Ok::<String, String>(time.format("%Y-%m-%d").to_string())
|
||||
})?;
|
||||
Ok(timestamp)
|
||||
}
|
||||
|
||||
#[procedure]
|
||||
fn export_timetrack(ctx: &mut ProcedureContext, username: String, date: String) -> Result<String, String> {
|
||||
// CEST only rn
|
||||
let start = chrono::NaiveDate::parse_from_str(&date, "%Y-%m-%d").unwrap().and_hms(5, 0, 0);
|
||||
let start_local = chrono_tz::Europe::Vienna.from_local_datetime(&start).unwrap();
|
||||
let start_utc = start_local.with_timezone(&chrono::Utc);
|
||||
let start_timestamp = Timestamp::from_micros_since_unix_epoch(start_utc.timestamp_micros());
|
||||
|
||||
let end = chrono::NaiveDate::parse_from_str(&date, "%Y-%m-%d").unwrap().and_hms(4, 0, 0) + Duration::from_hours(24);
|
||||
let end_local = chrono_tz::Europe::Vienna.from_local_datetime(&end).unwrap();
|
||||
let end_utc = end_local.with_timezone(&chrono::Utc);
|
||||
let end_timestamp = Timestamp::from_micros_since_unix_epoch(end_utc.timestamp_micros());
|
||||
|
||||
info!("start_of_day: {}", start_timestamp);
|
||||
info!("end_of_day: {}", end_timestamp);
|
||||
|
||||
let mut export = String::new();
|
||||
export.push_str(&format!("# export for {}\n\n", date));
|
||||
export.push_str(&format!("## actions\n"));
|
||||
|
||||
let profile = ctx.with_tx(|tx| tx.db.profiles().username().find(&username)).ok_or(format!("Profile with username {} not found", username))?;
|
||||
|
||||
let events = ctx.with_tx(|tx|
|
||||
tx.db.log_events()
|
||||
.log_events_index()
|
||||
.filter((profile.identity, start_timestamp..end_timestamp))
|
||||
);
|
||||
for event in events {
|
||||
let start = event.start
|
||||
.to_chrono_date_time()
|
||||
.map_err(|err| err.to_string())?
|
||||
.with_timezone(&Europe::Vienna)
|
||||
.format("%H:%M:%S");
|
||||
let end = event.end
|
||||
.to_chrono_date_time()
|
||||
.map_err(|err| err.to_string())?
|
||||
.with_timezone(&Europe::Vienna)
|
||||
.format("%H:%M:%S");
|
||||
|
||||
let line = format!("{} to {} ... {}\n", start, end, event.action.name);
|
||||
export.push_str(line.as_str());
|
||||
}
|
||||
|
||||
export.push_str("\n");
|
||||
export.push_str("## thoughts\n");
|
||||
let thoughts = ctx.with_tx(|tx| {
|
||||
tx.db.thoughts().time().filter(start_timestamp..end_timestamp)
|
||||
});
|
||||
for thought in thoughts {
|
||||
let line = format!("- {}\n\n", thought.text);
|
||||
export.push_str(line.as_str());
|
||||
}
|
||||
|
||||
Ok(export)
|
||||
}
|
||||
|
||||
|
||||
#[procedure]
|
||||
pub fn migrate(ctx: &mut ProcedureContext) -> String {
|
||||
info!("migrate procedure called");
|
||||
//let msg = "Nothing to migrate".to_string();
|
||||
|
||||
let msg: Result<String, String> = ctx.try_with_tx(|tx| {
|
||||
let mut out = String::new();
|
||||
let mut count = 0;
|
||||
//for event in tx.db.log_events_tmp().iter() {
|
||||
//count += 1;
|
||||
//}
|
||||
out.push_str(format!("Migrated {} rows................", count).as_str());
|
||||
Ok(out)
|
||||
});
|
||||
|
||||
return match msg {
|
||||
Ok(msg) => msg,
|
||||
Err(err) => err,
|
||||
};
|
||||
}
|
||||
|
||||
#[procedure]
|
||||
pub fn test(ctx: &mut ProcedureContext) -> String {
|
||||
let mut s = String::from("hoooooooooooooooooooooo\n");
|
||||
let a: String = ctx.with_tx(|tx| {
|
||||
let mut s = String::new();
|
||||
|
||||
|
||||
|
||||
|
||||
return s;
|
||||
});
|
||||
s.push_str(&format!("{}\n\n", a));
|
||||
s.push_str("\n");
|
||||
s.push_str(&format!("sender: {}", ctx.sender()));
|
||||
|
||||
s
|
||||
}
|
||||
|
||||
#[handler]
|
||||
pub fn test_handler(ctx: &mut HandlerContext, _req: Request<Body>) -> Response<Body> {
|
||||
let res = ctx.try_with_tx(|tx| {
|
||||
let mut s = String::new();
|
||||
for a in tx.db.profiles().iter() {
|
||||
s.push_str(&format!("{} ... {}\n", a.identity, a.username));
|
||||
}
|
||||
s.push_str(&format!("sender: {}", tx.sender()));
|
||||
return Ok(s);
|
||||
});
|
||||
return Response::new(Body::from_bytes(match res {
|
||||
Ok(body) => body,
|
||||
Err(e) => e,
|
||||
}));
|
||||
}
|
||||
|
||||
#[spacetimedb::http::router]
|
||||
fn router() -> Router {
|
||||
Router::new()
|
||||
.get("/test", test_handler)
|
||||
}
|
||||
671
marts/src/auth/client.rs
Normal file
671
marts/src/auth/client.rs
Normal file
@@ -0,0 +1,671 @@
|
||||
use chrono::{DateTime, TimeDelta, Utc};
|
||||
use mize::out;
|
||||
use mize::{
|
||||
item::ItemData, mize_err, mize_part, Mize, MizeError, MizePart, MizePartCreate, MizeResult,
|
||||
};
|
||||
use openidconnect::{
|
||||
ClientId, DeviceAuthorizationResponse, EmptyExtraDeviceAuthorizationFields, IssuerUrl,
|
||||
OAuth2TokenResponse, RefreshToken, Scope, TokenResponse,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fs::OpenOptions;
|
||||
use std::io::Write;
|
||||
use std::{path::PathBuf, thread};
|
||||
use tracing::info;
|
||||
|
||||
// console_log macro
|
||||
// that can be copied into other files for debugging purposes
|
||||
#[cfg(feature = "target-obsidian")]
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
#[cfg(feature = "target-obsidian")]
|
||||
#[wasm_bindgen]
|
||||
extern "C" {
|
||||
#[wasm_bindgen(js_namespace = console)]
|
||||
fn log(s: &str);
|
||||
}
|
||||
|
||||
#[cfg(feature = "target-obsidian")]
|
||||
macro_rules! console_log {
|
||||
// Note that this is using the `log` function imported above during
|
||||
// `bare_bones`
|
||||
($($t:tt)*) => (unsafe { log(&format_args!($($t)*).to_string())})
|
||||
}
|
||||
|
||||
#[cfg(feature = "target-os")]
|
||||
use clap::Command;
|
||||
|
||||
#[derive(Default)]
|
||||
#[mize_part]
|
||||
pub struct ClientAuth {
|
||||
mize: Mize,
|
||||
secrets: Option<ClientSecrets>,
|
||||
}
|
||||
|
||||
#[derive(Default, Serialize, Deserialize, Clone, Debug)]
|
||||
pub struct ClientSecrets {
|
||||
id_token: String,
|
||||
access_token: String,
|
||||
refresh_token: String,
|
||||
expiry: DateTime<Utc>,
|
||||
}
|
||||
|
||||
impl MizePartCreate for ClientAuth {}
|
||||
|
||||
impl MizePart for ClientAuth {
|
||||
fn name(&self) -> &'static str {
|
||||
"client_auth"
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "target-js")]
|
||||
pub fn store_secrets_js(mize: &Mize, secrets: &ClientSecrets) -> MizeResult<()> {
|
||||
use web_sys::window;
|
||||
|
||||
// Serialize struct to JSON string
|
||||
let serialized = serde_json::to_string(&secrets)?;
|
||||
|
||||
// Get browser window object
|
||||
let window = window().ok_or_else(|| mize_err!("No global window found"))?;
|
||||
|
||||
// Access local storage
|
||||
let storage = window
|
||||
.local_storage().map_err(|_| mize_err!("couldn't get local_storage"))?
|
||||
.ok_or_else(|| mize_err!("LocalStorage is not available"))?;
|
||||
|
||||
// Store the JSON string
|
||||
storage.set_item("mize_client_secrets", &serialized).map_err(|_| mize_err!("couldn't set item in local_storage"))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
impl ClientAuth {
|
||||
pub fn secrets(&self) -> Option<&ClientSecrets> {
|
||||
self.secrets.as_ref()
|
||||
}
|
||||
|
||||
pub fn id_token(&self) -> MizeResult<&str> {
|
||||
if self.secrets.is_none() {
|
||||
return Err(mize_err!("Client not logged in"));
|
||||
}
|
||||
Ok(&self.secrets.as_ref().unwrap().id_token)
|
||||
}
|
||||
|
||||
pub fn access_token(&self) -> MizeResult<&str> {
|
||||
if self.secrets.is_none() {
|
||||
return Err(mize_err!("Client not logged in"));
|
||||
}
|
||||
Ok(&self.secrets.as_ref().unwrap().access_token)
|
||||
}
|
||||
|
||||
pub fn refresh_token(&self) -> MizeResult<&str> {
|
||||
if self.secrets.is_none() {
|
||||
return Err(mize_err!("Client not logged in"));
|
||||
}
|
||||
Ok(&self.secrets.as_ref().unwrap().refresh_token)
|
||||
}
|
||||
|
||||
pub fn refresh(&mut self) -> MizeResult<()> {
|
||||
#[cfg(feature = "target-js")] {
|
||||
return Err(mize_err!("refresh is not supported on wasm"));
|
||||
}
|
||||
|
||||
#[cfg(feature = "target-os")] {
|
||||
let secrets = self.secrets.clone().ok_or(mize_err!(
|
||||
"no client secrets, meaning client_auth.refresh() thread can't use the refresh token"
|
||||
))?;
|
||||
let new_secrets = self.mize.spawn_async_blocking(
|
||||
"auth.refresh_flow",
|
||||
refresh_flow(self.mize.clone(), secrets),
|
||||
)?;
|
||||
store_secrets(&mut self.mize, &new_secrets)?;
|
||||
self.secrets = Some(new_secrets);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn login(&mut self) -> MizeResult<()> {
|
||||
#[cfg(feature = "target-os")] {
|
||||
if self.secrets().is_none() {
|
||||
let secrets = self
|
||||
.mize
|
||||
.spawn_async_blocking("auth.login_flow", auth_flow(self.mize.clone()))?;
|
||||
store_secrets(&mut self.mize, &secrets)?;
|
||||
self.secrets = Some(secrets);
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "target-js")] {
|
||||
if self.secrets().is_none() {
|
||||
let mut mize_clone = self.mize.clone();
|
||||
wasm_bindgen_futures::spawn_local(async move {
|
||||
console_log!("before auth_flow");
|
||||
let secrets = match auth_flow(mize_clone.clone()).await {
|
||||
Ok(secrets) => secrets,
|
||||
Err(e) => {
|
||||
mize_clone.report_err(e);
|
||||
return ();
|
||||
}
|
||||
};
|
||||
console_log!("after auth_flow");
|
||||
if let Err(e) = store_secrets_js(&mut mize_clone, &secrets) {
|
||||
mize_clone.report_err(e);
|
||||
return ();
|
||||
}
|
||||
let mut auth = match mize_clone.get_part_native::<ClientAuth>("client_auth") {
|
||||
Ok(auth) => auth,
|
||||
Err(e) => {
|
||||
mize_clone.report_err(e);
|
||||
return ();
|
||||
}
|
||||
};
|
||||
auth.secrets = Some(secrets);
|
||||
});
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn start_refresh_thread(&mut self) -> MizeResult<()> {
|
||||
|
||||
// background thread for os targets
|
||||
#[cfg(not(feature = "target-js"))]
|
||||
{
|
||||
let mut mize_clone = self.mize.clone();
|
||||
self.mize.spawn_background("auth.refresh", move || loop {
|
||||
info!("hi from auth.refresh thread...");
|
||||
thread::sleep(Duration::from_secs(33)); // TERRIBLE hack
|
||||
let auth = mize_clone.get_part_native::<ClientAuth>("client_auth")?;
|
||||
let expiry = auth.secrets.as_ref().unwrap().expiry.clone();
|
||||
info!("auth.refresh thread: now: {}", Utc::now());
|
||||
info!("auth.refresh thread: expiry: {}", expiry);
|
||||
let to_sleep = expiry - Utc::now() - TimeDelta::minutes(20);
|
||||
/*
|
||||
if to_sleep < TimeDelta::from_std(Duration::from_secs(0))? {
|
||||
if let Err(e) = auth.refresh() {
|
||||
e.log();
|
||||
}
|
||||
}
|
||||
*/
|
||||
drop(auth);
|
||||
|
||||
info!("auth.refresh: thread going to sleep for: {}", to_sleep);
|
||||
thread::sleep(to_sleep.to_std()?);
|
||||
|
||||
info!("refresh_thread: sleeping done");
|
||||
let mut auth = mize_clone.get_part_native::<ClientAuth>("auth")?;
|
||||
auth.refresh()?;
|
||||
drop(auth);
|
||||
})?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// spawn an async background task doing the refresh
|
||||
#[cfg(feature = "target-js")] {
|
||||
use wasm_bindgen::prelude::*;
|
||||
use wasm_bindgen_futures::JsFuture;
|
||||
|
||||
// Get the window object
|
||||
let window = web_sys::window().expect("no global `window` exists");
|
||||
|
||||
// Clone mize for the closure
|
||||
let mize_clone = self.mize.clone();
|
||||
|
||||
// Create a closure that will be called periodically
|
||||
let refresh_closure = Closure::wrap(Box::new(move || {
|
||||
let mut mize_clone = mize_clone.clone();
|
||||
wasm_bindgen_futures::spawn_local(async move {
|
||||
let mut auth = mize_clone.get_part_native::<ClientAuth>("client_auth");
|
||||
match auth {
|
||||
Ok(mut auth_part) => {
|
||||
if let Some(secrets) = auth_part.secrets() {
|
||||
let now = Utc::now();
|
||||
let expiry = secrets.expiry;
|
||||
let time_until_expiry = expiry - now;
|
||||
|
||||
// Refresh if token will expire in less than 20 minutes
|
||||
if time_until_expiry < TimeDelta::minutes(20) {
|
||||
if let Err(e) = auth_part.refresh() {
|
||||
console_log!("Error refreshing token: {:?}", e);
|
||||
} else {
|
||||
console_log!("Token refreshed successfully");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
console_log!("Error getting auth part: {:?}", e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}) as Box<dyn FnMut()>);
|
||||
|
||||
// Set up interval to run every 50 minutes (300000 ms)
|
||||
let interval_ms = 3000000;
|
||||
let _ = window.set_interval_with_callback_and_timeout_and_arguments_0(
|
||||
refresh_closure.as_ref().unchecked_ref(),
|
||||
interval_ms,
|
||||
);
|
||||
|
||||
// Leak the closure to prevent it from being dropped
|
||||
refresh_closure.forget();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
pub fn part_client_auth(mize: &mut Mize) -> MizeResult<()> {
|
||||
let mize_clone = mize.clone();
|
||||
|
||||
let mut auth_part = ClientAuth {
|
||||
secrets: None,
|
||||
mize: mize.clone(),
|
||||
};
|
||||
|
||||
let mut secrets = match load_secrets(mize) {
|
||||
Ok(secrets) => Some(secrets),
|
||||
Err(err) => {
|
||||
tracing::warn!("err reading secrets: {:?}", err);
|
||||
None
|
||||
}
|
||||
};
|
||||
auth_part.secrets = secrets.clone();
|
||||
|
||||
// do a refresh if the tokens have expired
|
||||
if secrets.is_some() && secrets.clone().unwrap().expiry < Utc::now() {
|
||||
auth_part.refresh()?;
|
||||
}
|
||||
|
||||
|
||||
// start refresh tread if configured
|
||||
if mize
|
||||
.get_config("auth.client.refresh_thread")
|
||||
.unwrap_or(ItemData::new())
|
||||
.value_bool()
|
||||
.unwrap_or(false)
|
||||
{
|
||||
auth_part.start_refresh_thread()?;
|
||||
}
|
||||
|
||||
|
||||
// cli command for os
|
||||
#[cfg(feature = "target-os")] {
|
||||
let mut cli = mize.get_part_native::<crate::CliPart>("cli")?;
|
||||
cli.set_command("ppc");
|
||||
cli.subcommand(Command::new("login"), move |_, mut mize| {
|
||||
let mut auth = mize.get_part_native::<ClientAuth>("client_auth")?;
|
||||
let secrets = if auth.secrets.is_some() {
|
||||
match mize.spawn_async_blocking(
|
||||
"client_auth",
|
||||
refresh_flow(mize.clone(), auth.secrets.clone().unwrap()),
|
||||
) {
|
||||
Ok(secrets) => secrets,
|
||||
Err(err) => {
|
||||
out!("err refreshing secrets: {:?}", err);
|
||||
mize.spawn_async_blocking("client_auth", auth_flow(mize_clone.clone()))?
|
||||
}
|
||||
}
|
||||
} else {
|
||||
mize.spawn_async_blocking("client_auth", auth_flow(mize_clone.clone()))?
|
||||
};
|
||||
store_secrets(&mut mize, &secrets)?;
|
||||
auth.secrets = Some(secrets);
|
||||
Ok(())
|
||||
})?;
|
||||
}
|
||||
|
||||
|
||||
// register command for obsidian
|
||||
#[cfg(feature = "target-obsidian")] {
|
||||
use crate::MartsCommand;
|
||||
|
||||
let obsidian = mize.get_part_native::<crate::ObsidianPart>("obsidian")?;
|
||||
obsidian.add_command(MartsCommand {
|
||||
id: "mize-login".to_string(),
|
||||
name: "Mize Login".to_string(),
|
||||
callback: Box::new(|mut mize| {
|
||||
let mut auth = mize.get_part_native::<crate::ClientAuth>("client_auth")?;
|
||||
let res = auth.login();
|
||||
console_log!("auth.login done: {:?}", res);
|
||||
res
|
||||
}),
|
||||
})?;
|
||||
}
|
||||
|
||||
|
||||
auth_part.secrets = secrets;
|
||||
|
||||
mize.register_part(Box::new(auth_part))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn load_secrets(mize: &mut Mize) -> MizeResult<ClientSecrets> {
|
||||
#[cfg(feature = "target-os")] {
|
||||
let mize_dir = mize.get_config("data_dir")?.value_string()?;
|
||||
let secrets_file_path = PathBuf::from(mize_dir).join("client_secrets.json");
|
||||
|
||||
let secrets_string = std::fs::read_to_string(secrets_file_path)?;
|
||||
let secrets: ClientSecrets = serde_json::from_str(&secrets_string)?;
|
||||
return Ok(secrets);
|
||||
}
|
||||
|
||||
#[cfg(feature = "target-js")] {
|
||||
use web_sys::window;
|
||||
|
||||
// Get browser window object
|
||||
let window = window().ok_or_else(|| mize_err!("No global window found"))?;
|
||||
|
||||
// Access local storage
|
||||
let storage = window
|
||||
.local_storage().map_err(|_| mize_err!("couldn't get local_storage"))?
|
||||
.ok_or_else(|| mize_err!("LocalStorage is not available"))?;
|
||||
|
||||
// Retrieve the string item
|
||||
return match storage.get_item("mize_client_secrets").map_err(|_| mize_err!("couldn't get item from local_storage"))? {
|
||||
Some(json_str) => {
|
||||
// Deserialize JSON string back into Client struct
|
||||
let secrets: ClientSecrets = serde_json::from_str(json_str.as_str())?;
|
||||
Ok(secrets)
|
||||
}
|
||||
None => Err(mize_err!("no secrets")), // Key does not exist
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fn store_secrets(mize: &mut Mize, secrets: &ClientSecrets) -> MizeResult<()> {
|
||||
|
||||
#[cfg(feature = "target-os")] {
|
||||
let mize_dir = mize.get_config("store")?.value_string()?;
|
||||
let secrets_file_path = PathBuf::from(mize_dir.clone())
|
||||
.join("misc")
|
||||
.join("client_secrets.json");
|
||||
std::fs::create_dir_all(PathBuf::from(mize_dir).join("misc"))?;
|
||||
|
||||
let secrets_string = serde_json::to_string(secrets)?;
|
||||
log::info!("secrets_file_path: {}", secrets_file_path.display());
|
||||
//std::fs::write(secrets_file_path, secrets_string)?;
|
||||
OpenOptions::new()
|
||||
.write(true)
|
||||
.create(true)
|
||||
.open(secrets_file_path)?
|
||||
.write_all(&secrets_string.as_bytes())?;
|
||||
|
||||
// update the spacetimedb_token in XDG_CONFIG_HOME/spacetime/cli.toml config file if configured to do so
|
||||
if mize
|
||||
.get_config("auth.client.update_spacetime_token")
|
||||
.unwrap_or(ItemData::empty())
|
||||
.as_bool()
|
||||
.unwrap_or(false)
|
||||
{
|
||||
update_spacetimedb_token(&secrets.id_token.as_str())?;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
use openidconnect::reqwest::Client as HttpClient;
|
||||
use openidconnect::DeviceAuthorizationUrl;
|
||||
use std::time::Duration;
|
||||
//use wasm_bindgen::JsCast;
|
||||
|
||||
|
||||
async fn refresh_flow(mut mize: Mize, old_secrets: ClientSecrets) -> MizeResult<ClientSecrets> {
|
||||
out!("Refresh flow...");
|
||||
let issuer = mize.get_config("auth.client.issuer")?.value_string()?;
|
||||
let client_id = mize.get_config("auth.client.client_id")?.value_string()?;
|
||||
|
||||
let issuer = IssuerUrl::new(issuer)?;
|
||||
let client_id = ClientId::new(client_id);
|
||||
|
||||
let http_client = HttpClient::new();
|
||||
|
||||
// Teach openidconnect-rs about the device authorization url.
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
struct MyExtraProviderMetadata {
|
||||
device_authorization_endpoint: String,
|
||||
}
|
||||
use openidconnect::core::{
|
||||
CoreAuthDisplay, CoreClaimName, CoreClaimType, CoreClient, CoreClientAuthMethod,
|
||||
CoreGrantType, CoreJsonWebKey, CoreJweContentEncryptionAlgorithm,
|
||||
CoreJweKeyManagementAlgorithm, CoreResponseMode, CoreResponseType,
|
||||
CoreSubjectIdentifierType,
|
||||
};
|
||||
use openidconnect::AdditionalProviderMetadata;
|
||||
use openidconnect::ProviderMetadata;
|
||||
impl AdditionalProviderMetadata for MyExtraProviderMetadata {}
|
||||
type MyProviderMetadata = ProviderMetadata<
|
||||
MyExtraProviderMetadata,
|
||||
CoreAuthDisplay,
|
||||
CoreClientAuthMethod,
|
||||
CoreClaimName,
|
||||
CoreClaimType,
|
||||
CoreGrantType,
|
||||
CoreJweContentEncryptionAlgorithm,
|
||||
CoreJweKeyManagementAlgorithm,
|
||||
CoreJsonWebKey,
|
||||
CoreResponseMode,
|
||||
CoreResponseType,
|
||||
CoreSubjectIdentifierType,
|
||||
>;
|
||||
|
||||
// Discover provider metadata
|
||||
let provider_metadata: MyProviderMetadata =
|
||||
MyProviderMetadata::discover_async(issuer, &http_client).await?;
|
||||
|
||||
let device_auth_url = provider_metadata
|
||||
.additional_metadata()
|
||||
.device_authorization_endpoint
|
||||
.clone();
|
||||
|
||||
let client = CoreClient::from_provider_metadata(
|
||||
provider_metadata,
|
||||
client_id,
|
||||
None, // public client
|
||||
)
|
||||
.set_device_authorization_url(DeviceAuthorizationUrl::new(device_auth_url)?);
|
||||
|
||||
let refresh_token = RefreshToken::new(old_secrets.refresh_token);
|
||||
let res = client
|
||||
.exchange_refresh_token(&refresh_token)?
|
||||
.request_async(&http_client)
|
||||
.await?;
|
||||
let expires_in = res.expires_in().ok_or(mize_err!(
|
||||
"no expiry information was in token response of the refresh flow"
|
||||
))?;
|
||||
|
||||
return Ok(ClientSecrets {
|
||||
id_token: res.id_token().unwrap().to_string(),
|
||||
access_token: res.access_token().secret().to_owned(),
|
||||
refresh_token: res.refresh_token().unwrap().secret().to_owned(),
|
||||
expiry: Utc::now() + expires_in,
|
||||
});
|
||||
}
|
||||
|
||||
async fn auth_flow(mut mize: Mize) -> MizeResult<ClientSecrets> {
|
||||
let issuer = mize.get_config("auth.client.issuer")?.value_string()?;
|
||||
let client_id = mize.get_config("auth.client.client_id")?.value_string()?;
|
||||
|
||||
let issuer = IssuerUrl::new(issuer)?;
|
||||
let client_id = ClientId::new(client_id);
|
||||
|
||||
let http_client = HttpClient::new();
|
||||
|
||||
// Teach openidconnect-rs about the device authorization url.
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
struct MyExtraProviderMetadata {
|
||||
device_authorization_endpoint: String,
|
||||
}
|
||||
use openidconnect::core::{
|
||||
CoreAuthDisplay, CoreClaimName, CoreClaimType, CoreClient, CoreClientAuthMethod,
|
||||
CoreGrantType, CoreJsonWebKey, CoreJweContentEncryptionAlgorithm,
|
||||
CoreJweKeyManagementAlgorithm, CoreResponseMode, CoreResponseType,
|
||||
CoreSubjectIdentifierType,
|
||||
};
|
||||
use openidconnect::AdditionalProviderMetadata;
|
||||
use openidconnect::ProviderMetadata;
|
||||
impl AdditionalProviderMetadata for MyExtraProviderMetadata {}
|
||||
type MyProviderMetadata = ProviderMetadata<
|
||||
MyExtraProviderMetadata,
|
||||
CoreAuthDisplay,
|
||||
CoreClientAuthMethod,
|
||||
CoreClaimName,
|
||||
CoreClaimType,
|
||||
CoreGrantType,
|
||||
CoreJweContentEncryptionAlgorithm,
|
||||
CoreJweKeyManagementAlgorithm,
|
||||
CoreJsonWebKey,
|
||||
CoreResponseMode,
|
||||
CoreResponseType,
|
||||
CoreSubjectIdentifierType,
|
||||
>;
|
||||
|
||||
// Discover provider metadata
|
||||
let provider_metadata: MyProviderMetadata =
|
||||
MyProviderMetadata::discover_async(issuer, &http_client).await?;
|
||||
|
||||
let device_auth_url = provider_metadata
|
||||
.additional_metadata()
|
||||
.device_authorization_endpoint
|
||||
.clone();
|
||||
|
||||
let client = CoreClient::from_provider_metadata(
|
||||
provider_metadata,
|
||||
client_id,
|
||||
None, // public client
|
||||
)
|
||||
.set_device_authorization_url(DeviceAuthorizationUrl::new(device_auth_url)?);
|
||||
|
||||
// ===== DEVICE AUTH REQUEST =====
|
||||
let details: Result<DeviceAuthorizationResponse<EmptyExtraDeviceAuthorizationFields>, _> =
|
||||
client
|
||||
.exchange_device_code()
|
||||
.add_scope(Scope::new("openid".into()))
|
||||
.add_scope(Scope::new("profile".into()))
|
||||
.add_scope(Scope::new("offline_access".into()))
|
||||
.request_async(&http_client)
|
||||
.await;
|
||||
|
||||
let details = match details {
|
||||
Ok(val) => val,
|
||||
Err(err) => {
|
||||
out!("err: {:?}", err);
|
||||
out!("err: {}", err);
|
||||
panic!();
|
||||
}
|
||||
};
|
||||
|
||||
#[cfg(feature = "target-obsidian")] {
|
||||
console_log!("details: {:?}", details);
|
||||
use std::str::FromStr;
|
||||
|
||||
use js_sys::{Array, Function, JsString, Reflect};
|
||||
use mize::error::IntoMizeResultJsValue;
|
||||
use wasm_bindgen::convert::TryFromJsValue;
|
||||
|
||||
let obsidian = mize.get_part_native::<crate::ObsidianPart>("obsidian")?;
|
||||
let show_device_code = Reflect::get(&obsidian.plugin(), &JsValue::from_str("showDeviceCode")).mres()?;
|
||||
let args = Array::new();
|
||||
args.push(&JsValue::from_str(details.user_code().secret().as_str()));
|
||||
args.push(&JsValue::from_str(details.verification_uri_complete().unwrap().secret().as_str()));
|
||||
|
||||
Reflect::apply(&show_device_code.dyn_into::<Function>().mres()?, obsidian.plugin(), &args).mres()?;
|
||||
}
|
||||
|
||||
#[cfg(feature = "target-os")] {
|
||||
out!(
|
||||
"If your browser does not open automatically, open this URL in it:\n{}\n",
|
||||
details.verification_uri_complete().unwrap().secret()
|
||||
);
|
||||
out!(
|
||||
"Or enter this PPC login code:\n{}\n",
|
||||
details.user_code().secret()
|
||||
);
|
||||
|
||||
if let Some(_) = details.verification_uri_complete() {
|
||||
let _ = webbrowser::open(details.verification_uri_complete().unwrap().secret());
|
||||
}
|
||||
}
|
||||
|
||||
let token_response = client
|
||||
.exchange_device_access_token(&details)?
|
||||
.request_async(
|
||||
&http_client,
|
||||
async |dur: Duration| {
|
||||
#[cfg(feature = "target-os")] {
|
||||
tokio::time::sleep(dur).await;
|
||||
}
|
||||
#[cfg(feature = "target-js")] {
|
||||
console_log!("sleeping");
|
||||
use wasm_bindgen::prelude::*;
|
||||
use wasm_bindgen_futures::JsFuture;
|
||||
let promise = js_sys::Promise::new(&mut |resolve, _| {
|
||||
web_sys::window()
|
||||
.unwrap()
|
||||
.set_timeout_with_callback_and_timeout_and_arguments_0(&resolve, dur.as_millis() as i32)
|
||||
.unwrap();
|
||||
});
|
||||
|
||||
// Await the JS Promise like a Rust Future
|
||||
let _ = JsFuture::from(promise).await;
|
||||
|
||||
}
|
||||
},
|
||||
None,
|
||||
)
|
||||
.await?;
|
||||
|
||||
|
||||
// ===== TOKENS =====
|
||||
let id_token = token_response
|
||||
.extra_fields()
|
||||
.id_token()
|
||||
.ok_or(mize_err!("no id_token"))?;
|
||||
|
||||
let refresh = token_response
|
||||
.refresh_token()
|
||||
.ok_or(mize_err!("no refresh token present"))?;
|
||||
|
||||
let expires_in = token_response.expires_in().ok_or(mize_err!(
|
||||
"no expiry information was in token response of the refresh flow"
|
||||
))?;
|
||||
|
||||
Ok(ClientSecrets {
|
||||
id_token: id_token.to_string(),
|
||||
access_token: token_response.access_token().secret().to_string(),
|
||||
refresh_token: refresh.secret().to_owned(),
|
||||
expiry: Utc::now() + expires_in,
|
||||
})
|
||||
}
|
||||
|
||||
fn update_spacetimedb_token(new_token: &str) -> MizeResult<()> {
|
||||
// Resolve config directory
|
||||
let config_dir = match std::env::var("XDG_CONFIG_HOME") {
|
||||
Ok(val) => PathBuf::from(val),
|
||||
Err(_) => {
|
||||
let home = std::env::var("HOME")?;
|
||||
std::path::PathBuf::from(home).join(".config")
|
||||
}
|
||||
};
|
||||
|
||||
let file_path = config_dir.join("spacetime").join("cli.toml");
|
||||
|
||||
// Read existing file (or create empty document if it doesn't exist)
|
||||
let content = std::fs::read_to_string(&file_path).unwrap_or_default();
|
||||
let mut data = ItemData::from_toml(content.as_str())?;
|
||||
|
||||
// Update the field
|
||||
data.set_path("spacetimedb_token", new_token)?;
|
||||
|
||||
// Write back
|
||||
std::fs::write(&file_path, data.to_toml()?)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -9,22 +9,19 @@ use serde_json::{json, Value};
|
||||
|
||||
use dioxus::prelude::*;
|
||||
use mize::{mize_err, mize_part, Mize, MizeError, MizePart, MizePartCreate, MizeResult};
|
||||
use tracing::instrument::WithSubscriber;
|
||||
|
||||
use crate::cli::CliPart;
|
||||
use crate::habitica::Habitica;
|
||||
use crate::SpacetimePart;
|
||||
use generated::spacetime::{
|
||||
activitiesQueryTableAccess, next_action, remove_activity, task_partQueryTableAccess,
|
||||
update_activity, update_upcoming, Action, ActivitiesTableAccess, Activity, DbConnection,
|
||||
TaskPart, TaskPartTableAccess,
|
||||
Action, ActivitiesTableAccess, Activity, DbConnection, LogEventsTableAccess, TaskPart, TaskPartTableAccess, activitiesQueryTableAccess, next_action, remove_activity, task_partQueryTableAccess, update_activity, update_upcoming
|
||||
};
|
||||
use spacetimedb_sdk::{
|
||||
credentials, DbContext, Error, Event, Identity, Status, SubscriptionHandle, Table,
|
||||
TableWithPrimaryKey,
|
||||
};
|
||||
|
||||
mod dashboard;
|
||||
|
||||
#[mize_part]
|
||||
#[derive(Default)]
|
||||
pub struct C2vi {
|
||||
@@ -63,14 +60,6 @@ pub fn c2vi(mize: &mut Mize) -> MizeResult<()> {
|
||||
.as_bool()
|
||||
.unwrap_or(false)
|
||||
{
|
||||
// refresh the credentials every 23h
|
||||
// as this is basically running as a daemon
|
||||
use crate::auth::client::ClientAuth;
|
||||
let mut auth = mize.get_part_native::<ClientAuth>("auth")?;
|
||||
auth.refresh()?;
|
||||
auth.start_refresh_thread()?;
|
||||
drop(auth);
|
||||
|
||||
launch_c2vi_dashboard(mize);
|
||||
}
|
||||
|
||||
@@ -89,8 +78,35 @@ pub fn launch_c2vi_dashboard(mize: &mut Mize) {
|
||||
.launch(c2vi_dash_main);
|
||||
}
|
||||
|
||||
//use spacetimedb_sdk::spacetime_module::QueryBuilder;
|
||||
|
||||
/*
|
||||
pub fn use_spacetime<T: Fn(QueryBuilder)>(query: T) {
|
||||
let mut mize = use_context::<Mize>();
|
||||
let mut spacetime = mize.get_part_native::<SpacetimePart>("spacetime").unwrap();
|
||||
let con = spacetime.con();
|
||||
con.subscription_builder()
|
||||
.on_applied(move |ctx| {
|
||||
let data = ctx.db.task_part().identity().find(&ctx.identity()).unwrap();
|
||||
current.set(data.current.name.clone());
|
||||
})
|
||||
.add_query(query)
|
||||
.subscribe();
|
||||
con.db
|
||||
.task_part()
|
||||
.on_update(move |_, old: &TaskPart, new: &TaskPart| {
|
||||
current.set(new.current.name.clone());
|
||||
});
|
||||
}
|
||||
*/
|
||||
|
||||
//pub fn use_by_pk(identity: Identity)
|
||||
//pub fn use_one<T>
|
||||
|
||||
pub fn c2vi_dash_main() -> Element {
|
||||
let mut current = use_signal_sync(|| "".to_string());
|
||||
//TaskPart::
|
||||
//use_one()
|
||||
use_effect(move || {
|
||||
let mut mize = use_context::<Mize>();
|
||||
let mut spacetime = mize.get_part_native::<SpacetimePart>("spacetime").unwrap();
|
||||
@@ -100,7 +116,7 @@ pub fn c2vi_dash_main() -> Element {
|
||||
let data = ctx.db.task_part().identity().find(&ctx.identity()).unwrap();
|
||||
current.set(data.current.name.clone());
|
||||
})
|
||||
.add_query(|q| q.from.task_part())
|
||||
.add_query(|q| q.from.task_part().r#where(|t| t.identity.eq(con.identity())))
|
||||
.subscribe();
|
||||
con.db
|
||||
.task_part()
|
||||
@@ -167,7 +183,7 @@ fn setup_tables(conn: &Connection) -> MizeResult<()> {
|
||||
fn register_subcommands(cli: &mut CliPart) {
|
||||
cli.subcommand(
|
||||
Command::new("c2vi-dash").about("Run the c2vi dashboard gui"),
|
||||
|_matches, mut _mize| Ok(dioxus::launch(dashboard::main)),
|
||||
|_matches, mut _mize| Ok(dioxus::launch(super::dashboard::main)),
|
||||
);
|
||||
cli.subcommand(
|
||||
Command::new("bed").about("Command to run when c2vi goes to bed"),
|
||||
277
marts/src/cli/mod.rs
Normal file
277
marts/src/cli/mod.rs
Normal file
@@ -0,0 +1,277 @@
|
||||
use clap::{ArgMatches, Command};
|
||||
use dioxus::core::SpawnIfAsync;
|
||||
use std::{cell::RefCell, collections::HashMap};
|
||||
|
||||
use mize::{
|
||||
item::ItemData, mize_err, mize_part, proto::MizeMessage, Mize, MizeError, MizePart,
|
||||
MizePartCreate, MizeResult,
|
||||
};
|
||||
|
||||
thread_local! {
|
||||
pub static MIZE_CLI_SPAWN: RefCell<Option<CliSpawn>> = const { RefCell::new(None) };
|
||||
}
|
||||
|
||||
struct CliSpawn {
|
||||
wait_rx: flume::Receiver<()>,
|
||||
conn_id: Option<u64>,
|
||||
mize: Mize,
|
||||
}
|
||||
|
||||
pub fn cli_spawn_setup(mize: Mize, conn_id: u64, wait_rx: flume::Receiver<()>) -> MizeResult<()> {
|
||||
MIZE_CLI_SPAWN.with(|cell| {
|
||||
*cell.borrow_mut() = Some(CliSpawn {
|
||||
wait_rx,
|
||||
conn_id: Some(conn_id),
|
||||
mize,
|
||||
});
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
pub fn cli_spawn(cmd: String) -> MizeResult<()> {
|
||||
MIZE_CLI_SPAWN.with(|cell| {
|
||||
if let Some(spawn) = cell.borrow().as_ref() {
|
||||
let conn_id = spawn.conn_id.ok_or(mize_err!("no conn_id set"))?;
|
||||
let conn = spawn.mize.get_connection(conn_id)?;
|
||||
conn.send(MizeMessage::new_custom("cli.spawn", cmd, conn_id))?;
|
||||
|
||||
log::info!("before wait_rx.recv()");
|
||||
spawn.wait_rx.recv()?;
|
||||
log::info!("after wait_rx.recv()");
|
||||
Ok(())
|
||||
} else {
|
||||
Err(mize_err!("no cli spawn thread_local available"))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[mize_part("cli")]
|
||||
#[derive(Default)]
|
||||
pub struct CliPart {
|
||||
mize: Mize,
|
||||
cmds: HashMap<String, Command>,
|
||||
current_cmd_name: String,
|
||||
actions:
|
||||
HashMap<String, Box<dyn Fn(&ArgMatches, Mize) -> MizeResult<()> + Send + Sync + 'static>>,
|
||||
parsers: Option<Vec<Box<dyn Fn(Mize, Vec<String>) -> MizeResult<()> + Send + Sync + 'static>>>,
|
||||
}
|
||||
|
||||
pub fn cli(mize: &mut Mize) -> MizeResult<()> {
|
||||
let command = Command::new("marts-cli").allow_external_subcommands(true);
|
||||
let (spawn_wait_tx, spawn_wait_rx) = flume::unbounded::<()>();
|
||||
let mut cli_part = CliPart {
|
||||
mize: mize.clone(),
|
||||
cmds: HashMap::new(),
|
||||
current_cmd_name: "marts-cli".to_string(),
|
||||
actions: HashMap::new(),
|
||||
parsers: Some(Vec::new()),
|
||||
};
|
||||
cli_part.cmds.insert("marts-cli".to_string(), command);
|
||||
|
||||
mize.add_listener(
|
||||
"cli",
|
||||
Box::new(move |msg, mize| cli_msg_handler(msg, mize, spawn_wait_rx.clone())),
|
||||
)?;
|
||||
mize.add_listener(
|
||||
"cli.spawn_done",
|
||||
Box::new(move |mut msg, mize| {
|
||||
log::info!("before send");
|
||||
spawn_wait_tx.send(());
|
||||
log::info!("after send");
|
||||
Ok(())
|
||||
}),
|
||||
)?;
|
||||
mize.add_part(Box::new(cli_part))
|
||||
}
|
||||
|
||||
impl MizePartCreate for CliPart {}
|
||||
|
||||
impl MizePart for CliPart {
|
||||
fn init(&mut self, _mize: &mut Mize) -> MizeResult<()> {
|
||||
Ok(())
|
||||
}
|
||||
fn run(&mut self, mize: &mut Mize) -> MizeResult<()> {
|
||||
if mize
|
||||
.get_config("cli.daemonMode")
|
||||
.unwrap_or(ItemData::new())
|
||||
.as_bool()
|
||||
.unwrap_or(false)
|
||||
{
|
||||
return Ok(());
|
||||
}
|
||||
let matches = self
|
||||
.cmds
|
||||
.remove(&self.current_cmd_name)
|
||||
.unwrap()
|
||||
.get_matches();
|
||||
let sub_cmd = match matches.subcommand_name() {
|
||||
Some(sub_cmd) => sub_cmd,
|
||||
None => {
|
||||
log::info!("No subcommand provided");
|
||||
return Ok(());
|
||||
}
|
||||
};
|
||||
let action = match self.actions.remove(sub_cmd) {
|
||||
Some(action) => action,
|
||||
None => {
|
||||
// external parsers
|
||||
for parser in self.parsers.take().unwrap() {
|
||||
parser(
|
||||
mize.clone(),
|
||||
matches
|
||||
.subcommand_matches(sub_cmd)
|
||||
.unwrap()
|
||||
.get_many::<String>("")
|
||||
.unwrap()
|
||||
.map(|s| s.to_string())
|
||||
.collect::<Vec<String>>(),
|
||||
)?;
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
};
|
||||
action(matches.subcommand_matches(sub_cmd).unwrap(), mize.clone())?;
|
||||
Ok(())
|
||||
}
|
||||
fn opts(&self, mize: &mut Mize) {
|
||||
mize.new_opt("cli.name");
|
||||
}
|
||||
}
|
||||
|
||||
fn cli_msg_handler(
|
||||
mut msg: MizeMessage,
|
||||
mize: &mut Mize,
|
||||
spawn_wait_rx: flume::Receiver<()>,
|
||||
) -> MizeResult<()> {
|
||||
let binding = msg.data()?;
|
||||
let data = binding
|
||||
.cbor()
|
||||
.as_array()
|
||||
.ok_or(mize_err!("cli message data was not an array"))?;
|
||||
let full_cmd = data
|
||||
.iter()
|
||||
.nth(0)
|
||||
.cloned()
|
||||
.ok_or(mize_err!("cli message data was empty"))?;
|
||||
let full_cmd_text = full_cmd
|
||||
.as_text()
|
||||
.ok_or(mize_err!("cli cmd was not text"))?;
|
||||
let cmd = full_cmd_text
|
||||
.split("/")
|
||||
.last()
|
||||
.ok_or(mize_err!("failed to get cmd"))?;
|
||||
let mut cli = mize.get_part_native::<CliPart>("cli").unwrap();
|
||||
let command = cli
|
||||
.cmds
|
||||
.get(cmd)
|
||||
.ok_or(mize_err!("no command {cmd} in cli part"))?;
|
||||
let matches = command
|
||||
.clone()
|
||||
.get_matches_from(data.iter().map(|v| v.as_text().unwrap()));
|
||||
let sub_cmd_name = match matches.subcommand_name() {
|
||||
Some(sub_cmd) => sub_cmd,
|
||||
None => {
|
||||
cli.send_stderr(&msg, "No subcommand provided".to_string())?;
|
||||
return Ok(());
|
||||
}
|
||||
};
|
||||
let action = match cli.actions.get_mut(sub_cmd_name) {
|
||||
Some(action) => action,
|
||||
None => {
|
||||
return Ok(());
|
||||
}
|
||||
};
|
||||
|
||||
let original_msg = msg.clone();
|
||||
let mize_clone = mize.clone();
|
||||
mize::MIZE_STDOUT.with(move |stdout| {
|
||||
*stdout.borrow_mut() = Some(Box::new(move |string| {
|
||||
let conn_id = original_msg.conn_id;
|
||||
let msg = MizeMessage::new_custom("cli.stdout", string, conn_id);
|
||||
let conn = mize_clone.get_connection(conn_id)?;
|
||||
conn.send(msg)?;
|
||||
Ok(())
|
||||
}))
|
||||
});
|
||||
|
||||
cli_spawn_setup(mize.clone(), msg.conn_id, spawn_wait_rx)?;
|
||||
|
||||
action(
|
||||
matches.subcommand_matches(sub_cmd_name).unwrap(),
|
||||
mize.clone(),
|
||||
)?;
|
||||
cli.send_done(&msg)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
impl CliPart {
|
||||
pub fn set_command(&mut self, cmd: &str) {
|
||||
self.current_cmd_name = cmd.to_owned();
|
||||
if !self.cmds.contains_key(cmd) {
|
||||
self.cmds
|
||||
.insert(cmd.to_owned(), Command::new(cmd.to_owned()));
|
||||
}
|
||||
}
|
||||
pub fn send_stderr(&mut self, original_msg: &MizeMessage, msg: String) -> MizeResult<()> {
|
||||
let conn_id = original_msg.conn_id;
|
||||
let msg = MizeMessage::new_custom("cli.stderr", msg, conn_id);
|
||||
let conn = self.mize.get_connection(conn_id)?;
|
||||
conn.send(msg)?;
|
||||
Ok(())
|
||||
}
|
||||
pub fn send_stdout(&mut self, original_msg: &MizeMessage, msg: String) -> MizeResult<()> {
|
||||
let conn_id = original_msg.conn_id;
|
||||
let msg = MizeMessage::new_custom("cli.stdout", msg, conn_id);
|
||||
let conn = self.mize.get_connection(conn_id)?;
|
||||
conn.send(msg)?;
|
||||
Ok(())
|
||||
}
|
||||
pub fn send_spawn(&mut self, original_msg: &MizeMessage, msg: String) -> MizeResult<()> {
|
||||
let conn_id = original_msg.conn_id;
|
||||
let msg = MizeMessage::new_custom("cli.spawn", msg, conn_id);
|
||||
let conn = self.mize.get_connection(conn_id)?;
|
||||
conn.send(msg)?;
|
||||
Ok(())
|
||||
}
|
||||
pub fn send_done(&mut self, original_msg: &MizeMessage) -> MizeResult<()> {
|
||||
let conn_id = original_msg.conn_id;
|
||||
let msg = MizeMessage::new_custom("cli.done", ItemData::new(), conn_id);
|
||||
let conn = self.mize.get_connection(conn_id)?;
|
||||
conn.send(msg)?;
|
||||
Ok(())
|
||||
}
|
||||
pub fn subcommand<T: Fn(&ArgMatches, Mize) -> MizeResult<()> + Send + Sync + 'static>(
|
||||
&mut self,
|
||||
subcmd: Command,
|
||||
action: T,
|
||||
) -> MizeResult<()> {
|
||||
let name = subcmd.get_name().to_string();
|
||||
let mut cmd = self
|
||||
.cmds
|
||||
.remove(&self.current_cmd_name)
|
||||
.unwrap()
|
||||
.subcommand(subcmd);
|
||||
self.cmds.insert(self.current_cmd_name.clone(), cmd);
|
||||
self.actions.insert(name, Box::new(action));
|
||||
Ok(())
|
||||
}
|
||||
pub fn add_sub_parser<T: Fn(Mize, Vec<String>) -> MizeResult<()> + Send + Sync + 'static>(
|
||||
&mut self,
|
||||
parser: T,
|
||||
) -> MizeResult<()> {
|
||||
self.parsers.as_mut().unwrap().push(Box::new(parser));
|
||||
Ok(())
|
||||
}
|
||||
pub fn with_cmd(
|
||||
&mut self,
|
||||
func: fn(mize: Mize, cmd: Command) -> MizeResult<Command>,
|
||||
) -> MizeResult<()> {
|
||||
let cmd = self.cmds.remove(&self.current_cmd_name).ok_or(mize_err!(
|
||||
"current command {} not found",
|
||||
self.current_cmd_name
|
||||
))?;
|
||||
let cmd = func(self.mize.clone(), cmd)?;
|
||||
self.cmds.insert(self.current_cmd_name.clone(), cmd);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -61,7 +61,7 @@ impl Habitica {
|
||||
if !response.status().is_success() {
|
||||
let status = response.status();
|
||||
let text = response.text().unwrap_or_default();
|
||||
println!("request_url: {}", url);
|
||||
log::info!("request_url: {}", url);
|
||||
return Err(mize_err!(
|
||||
"Habitica API error: {} {} - {}",
|
||||
status.as_u16(),
|
||||
@@ -102,7 +102,7 @@ fn handle_rate_limit(response: &Response) {
|
||||
.get("X-RateLimit-Reset")
|
||||
.and_then(|v| v.to_str().ok());
|
||||
|
||||
println!(
|
||||
log::info!(
|
||||
"RateLimit: {} | Remaining: {} | Reset: {}",
|
||||
limit,
|
||||
remaining,
|
||||
@@ -116,7 +116,7 @@ fn handle_rate_limit(response: &Response) {
|
||||
let wait_duration = reset_date.signed_duration_since(now).to_std();
|
||||
if let Ok(wait_duration) = wait_duration {
|
||||
let wait_ms = wait_duration.as_millis() as u64 + 1000;
|
||||
println!(
|
||||
log::info!(
|
||||
"Waiting {} secs for next rate limit window...",
|
||||
(wait_ms / 1000)
|
||||
);
|
||||
@@ -9,7 +9,7 @@ use crate::JsPart;
|
||||
|
||||
#[op2(fast)]
|
||||
fn op_mize_get_part(#[string] name: &str) {
|
||||
println!("js wants the part {}", name);
|
||||
log::info!("js wants the part {}", name);
|
||||
}
|
||||
|
||||
#[op2]
|
||||
@@ -22,7 +22,7 @@ fn op_mize_get_config(state: &mut OpState, #[string] key: &str) -> String {
|
||||
#[op2(fast)]
|
||||
fn op_mize_add_part(state: &mut OpState, #[string] key: &str) {
|
||||
let mize: Mize = state.borrow::<Mize>().clone();
|
||||
println!("adding part {}", key);
|
||||
log::info!("adding part {}", key);
|
||||
}
|
||||
|
||||
#[op2(fast)]
|
||||
@@ -31,7 +31,7 @@ pub type BoxClosure = Box<dyn for<'a> FnOnce(&'a mut JsRuntime) -> BoxFuture<'a>
|
||||
|
||||
impl MizePart for JsPart {
|
||||
fn run(&mut self, mize: &mut Mize) -> MizeResult<()> {
|
||||
println!("js part run");
|
||||
log::info!("js part run");
|
||||
self.sender
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
@@ -100,27 +100,27 @@ fn js_runtime_thread(
|
||||
.unwrap();
|
||||
|
||||
loop {
|
||||
println!("js thread waiting for smth");
|
||||
log::info!("js thread waiting for smth");
|
||||
let msg = receiver.recv().unwrap();
|
||||
println!("js thread got smth");
|
||||
log::info!("js thread got smth");
|
||||
match msg {
|
||||
JsRuntimeThreadMessage::RunInitJs(js_code) => {
|
||||
if let Err(err) = js_runtime.execute_script("[init]", js_code) {
|
||||
println!("err: {}", err);
|
||||
log::info!("err: {}", err);
|
||||
}
|
||||
println!("done running js");
|
||||
log::info!("done running js");
|
||||
}
|
||||
JsRuntimeThreadMessage::RunJs(js_code) => {
|
||||
if let Err(err) = js_runtime.execute_script("[idk]", js_code) {
|
||||
println!("err: {}", err);
|
||||
log::info!("err: {}", err);
|
||||
}
|
||||
println!("done running js");
|
||||
log::info!("done running js");
|
||||
}
|
||||
JsRuntimeThreadMessage::DoRunPhase => {
|
||||
if let Err(err) = js_runtime.execute_script("[runPhase]", "mize.runPhase()") {
|
||||
println!("err: {}", err);
|
||||
log::info!("err: {}", err);
|
||||
}
|
||||
println!("done with runPhase");
|
||||
log::info!("done with runPhase");
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
#![feature(oneshot_channel)]
|
||||
|
||||
#[cfg(feature = "target-os")]
|
||||
mod cli;
|
||||
pub mod cli;
|
||||
#[cfg(feature = "target-os")]
|
||||
pub use cli::*;
|
||||
|
||||
@@ -9,13 +11,19 @@ pub mod habitica;
|
||||
pub use habitica::*;
|
||||
|
||||
#[cfg(feature = "target-os")]
|
||||
pub mod c2vi;
|
||||
pub mod c2vi {
|
||||
pub mod part;
|
||||
pub mod dashboard;
|
||||
}
|
||||
#[cfg(feature = "target-os")]
|
||||
pub use c2vi::*;
|
||||
pub use c2vi::part::*;
|
||||
|
||||
#[cfg(feature = "target-obsidian")]
|
||||
pub mod obsidian;
|
||||
#[cfg(feature = "target-obsidian")]
|
||||
pub use obsidian::*;
|
||||
|
||||
#[cfg(feature = "target-os")]
|
||||
pub mod spacetime;
|
||||
#[cfg(feature = "target-os")]
|
||||
pub use spacetime::*;
|
||||
|
||||
pub mod auth {
|
||||
@@ -24,7 +32,13 @@ pub mod auth {
|
||||
}
|
||||
pub use auth::*;
|
||||
|
||||
pub mod ppc;
|
||||
|
||||
pub mod timetrack;
|
||||
|
||||
//#[cfg(feature = "target-os")]
|
||||
//pub mod js;
|
||||
//#[cfg(feature = "target-os")]
|
||||
//pub use js::*;
|
||||
|
||||
pub use generated;
|
||||
88
marts/src/obsidian.rs
Normal file
88
marts/src/obsidian.rs
Normal file
@@ -0,0 +1,88 @@
|
||||
use mize::{Mize, MizeResult, MizePart, MizePartCreate, mize_part};
|
||||
use wasm_bindgen::JsValue;
|
||||
use mize::error::IntoMizeResultJsValue;
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
|
||||
|
||||
// console_log macro
|
||||
|
||||
#[wasm_bindgen]
|
||||
extern "C" {
|
||||
#[wasm_bindgen(js_namespace = console)]
|
||||
fn log(s: &str);
|
||||
}
|
||||
|
||||
macro_rules! console_log {
|
||||
// Note that this is using the `log` function imported above during
|
||||
// `bare_bones`
|
||||
($($t:tt)*) => ({ log(&format_args!($($t)*).to_string())})
|
||||
}
|
||||
|
||||
pub fn part_obsidian(mize: &mut Mize, plugin: JsValue, app: JsValue) -> MizeResult<()> {
|
||||
|
||||
mize.register_part(Box::new(ObsidianPart {
|
||||
mize: mize.clone(),
|
||||
plugin,
|
||||
app,
|
||||
}));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
#[mize_part("obsidian")]
|
||||
#[derive(Default)]
|
||||
pub struct ObsidianPart {
|
||||
mize: Mize,
|
||||
plugin: JsValue, // the jsValue of the obsidian plugin
|
||||
app: JsValue, // the jsValue of the obsidian instance
|
||||
}
|
||||
|
||||
pub struct MartsCommand {
|
||||
pub id: String,
|
||||
pub name: String,
|
||||
pub callback: Box<dyn FnMut(Mize) -> MizeResult<()>>,
|
||||
}
|
||||
|
||||
impl MizePart for ObsidianPart {}
|
||||
impl MizePartCreate for ObsidianPart {}
|
||||
|
||||
impl ObsidianPart {
|
||||
pub fn plugin(&self) -> &JsValue {
|
||||
&self.plugin
|
||||
}
|
||||
pub fn add_command(&self, mut cmd: MartsCommand) -> MizeResult<()> {
|
||||
use wasm_bindgen::prelude::*;
|
||||
use wasm_bindgen::JsCast;
|
||||
use js_sys::{Function, Reflect, Object};
|
||||
let obsidian_cmd = Object::new();
|
||||
|
||||
Reflect::set(&obsidian_cmd, &"id".into(), &"login".into()).mres()?;
|
||||
Reflect::set(&obsidian_cmd, &"name".into(), &"Login".into()).mres()?;
|
||||
|
||||
// callback: () => run_login()
|
||||
let mize_clone = self.mize.clone();
|
||||
let cb = Closure::<dyn FnMut()>::wrap(Box::new(move || {
|
||||
if let Err(e) = (cmd.callback)(mize_clone.clone()) {
|
||||
mize_clone.report_err(e);
|
||||
}
|
||||
}));
|
||||
|
||||
Reflect::set(&obsidian_cmd, &"callback".into(), cb.as_ref()).mres()?;
|
||||
|
||||
// plugin.addCommand(cmd)
|
||||
let add_command_fn = Reflect::get(&self.plugin, &"addCommand".into()).mres()?
|
||||
.dyn_into::<Function>().mres()?;
|
||||
|
||||
add_command_fn
|
||||
.call1(&self.plugin, &obsidian_cmd).mres()?;
|
||||
|
||||
|
||||
// IMPORTANT: prevent callback from being dropped
|
||||
cb.forget();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
// PPC Website UI Module
|
||||
// This module contains the user interface components for the PPC website
|
||||
|
||||
use dioxus::prelude::*;
|
||||
|
||||
/// Main PPC website component
|
||||
|
||||
pub fn ppc_website() -> Element {
|
||||
pub fn main_page() -> Element {
|
||||
rsx! {
|
||||
"hello world"
|
||||
div {
|
||||
class: "min-h-screen bg-gray-100",
|
||||
class: "min-h-screen bg-gray-100 items-center flex h-screen items-center justify-center",
|
||||
|
||||
// Header
|
||||
header {
|
||||
4
marts/src/ppc/mod.rs
Normal file
4
marts/src/ppc/mod.rs
Normal file
@@ -0,0 +1,4 @@
|
||||
|
||||
pub mod main_page;
|
||||
|
||||
pub use main_page::main_page;
|
||||
107
marts/src/spacetime.rs
Normal file
107
marts/src/spacetime.rs
Normal file
@@ -0,0 +1,107 @@
|
||||
use std::sync::oneshot::Sender;
|
||||
|
||||
use mize::{mize_part, Mize, MizePart, MizePartCreate, MizeResult};
|
||||
|
||||
use crate::auth::client::ClientAuth;
|
||||
use generated::spacetime::DbConnection;
|
||||
|
||||
#[mize_part]
|
||||
#[derive(Default)]
|
||||
pub struct SpacetimePart {
|
||||
mize: Mize,
|
||||
con: Option<DbConnection>,
|
||||
}
|
||||
|
||||
impl MizePartCreate for SpacetimePart {}
|
||||
impl MizePart for SpacetimePart {
|
||||
fn name(&self) -> &'static str {
|
||||
"spacetime"
|
||||
}
|
||||
}
|
||||
|
||||
impl SpacetimePart {
|
||||
pub fn con_mut(&mut self) -> &mut DbConnection {
|
||||
self.con.as_mut().unwrap()
|
||||
}
|
||||
pub fn con(&self) -> &DbConnection {
|
||||
self.con.as_ref().unwrap()
|
||||
}
|
||||
// does not work, because i can't use the QueryBuilder type in my code.... it's private
|
||||
/*
|
||||
pub fn get<T>(
|
||||
&self,
|
||||
on_applied: impl FnOnce(&generated::spacetime::SubscriptionEventContext, Sender<T>)+ Send + 'static,
|
||||
query: impl FnOnce(QueryBuilder)+ Send + 'static
|
||||
) -> MizeResult<T> {
|
||||
let (done_tx, done_rx) = std::sync::oneshot::channel::<T>();
|
||||
use spacetimedb_sdk::DbContext;
|
||||
self.con().subscription_builder().on_applied(move |ctx| {
|
||||
on_applied(ctx, done_tx);
|
||||
}).add_query(build);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
pub fn spacetime(mize: &mut Mize) -> MizeResult<()> {
|
||||
let auth = mize.get_part_native::<ClientAuth>("client_auth")?;
|
||||
let db_url = mize.get_config("spacetime.url")?.value_string()?;
|
||||
|
||||
let con: Option<DbConnection>;
|
||||
|
||||
#[cfg(feature = "target-os")] {
|
||||
con = Some(DbConnection::builder()
|
||||
.with_token(Some(auth.id_token()?))
|
||||
.with_database_name("mize")
|
||||
.with_uri(db_url)
|
||||
//.on_connect(callback)
|
||||
//.on_disconnect(callback)
|
||||
//.on_connect_error(callback)
|
||||
//.with_compression(...)
|
||||
.build()?);
|
||||
con.as_ref().unwrap().run_threaded();
|
||||
}
|
||||
|
||||
#[cfg(feature = "target-js")] {
|
||||
let mut mize = mize.clone();
|
||||
con = None;
|
||||
wasm_bindgen_futures::spawn_local(async move {
|
||||
let id_token = match auth.id_token() {
|
||||
Ok(val) => val,
|
||||
Err(e) => {
|
||||
mize.report_err(e.into());
|
||||
return;
|
||||
}
|
||||
};
|
||||
let con = match DbConnection::builder()
|
||||
.with_token(Some(id_token))
|
||||
.with_database_name("mize")
|
||||
.with_uri(db_url)
|
||||
//.on_connect(callback)
|
||||
//.on_disconnect(callback)
|
||||
//.on_connect_error(callback)
|
||||
//.with_compression(...)
|
||||
.build().await {
|
||||
Ok(val) => val,
|
||||
Err(e) => {
|
||||
mize.report_err(e.into());
|
||||
return;
|
||||
}
|
||||
};
|
||||
let mut spacetime = match mize.get_part_native::<SpacetimePart>("spacetime") {
|
||||
Ok(val) => val,
|
||||
Err(e) => {
|
||||
mize.report_err(e.into());
|
||||
return;
|
||||
}
|
||||
};
|
||||
con.run_async();
|
||||
spacetime.con = Some(con);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
mize.register_part(Box::new(SpacetimePart {
|
||||
mize: mize.clone(),
|
||||
con: con,
|
||||
}))
|
||||
}
|
||||
@@ -1,6 +1,10 @@
|
||||
use std::sync::oneshot;
|
||||
|
||||
#[cfg(feature = "target-os")]
|
||||
use clap::{Arg, ArgAction, ArgMatches, Command};
|
||||
use generated::spacetime::{Profile, TaskPart};
|
||||
use mize::out;
|
||||
use mize::proto::MizeMessage;
|
||||
use mize::{Mize, MizeError, MizePart, MizePartCreate, MizeResult, mize_err, mize_part};
|
||||
use spacetimedb_sdk::{DbContext, Table};
|
||||
|
||||
@@ -9,7 +13,8 @@ use generated::spacetime::{
|
||||
activitiesQueryTableAccess, next_action, remove_activity, task_partQueryTableAccess,
|
||||
update_activity, update_upcoming,
|
||||
};
|
||||
use marts::spacetime::SpacetimePart;
|
||||
use crate::spacetime::SpacetimePart;
|
||||
use spacetimedb_sdk::TableWithPrimaryKey;
|
||||
|
||||
#[mize_part]
|
||||
#[derive(Default)]
|
||||
@@ -18,38 +23,120 @@ pub struct TimeTrack {
|
||||
}
|
||||
|
||||
pub fn timetrack(mize: &mut Mize) -> MizeResult<()> {
|
||||
let mut cli = mize.get_part_native::<marts::CliPart>("cli")?;
|
||||
|
||||
cli.subcommand(
|
||||
Command::new("editActionList").alias("aal").arg(
|
||||
Arg::new("defaultTemplate")
|
||||
.short('d')
|
||||
.action(ArgAction::SetTrue),
|
||||
),
|
||||
command_edit_action_list,
|
||||
);
|
||||
cli.subcommand(Command::new("nextAction").alias("ne"), command_next_action);
|
||||
cli.subcommand(
|
||||
Command::new("printCurrentAction").alias("cu"),
|
||||
command_current_action,
|
||||
);
|
||||
cli.subcommand(
|
||||
Command::new("listActions").alias("la"),
|
||||
command_list_actions,
|
||||
);
|
||||
cli.subcommand(
|
||||
Command::new("listActivities").alias("lac"),
|
||||
command_list_activities,
|
||||
);
|
||||
cli.subcommand(
|
||||
Command::new("editActivities").alias("eac"),
|
||||
command_edit_activities,
|
||||
);
|
||||
#[cfg(feature = "target-os")] {
|
||||
let mut cli = mize.get_part_native::<crate::CliPart>("cli")?;
|
||||
cli.set_command("ppc");
|
||||
|
||||
cli.subcommand(
|
||||
Command::new("editActionList").alias("aal").arg(
|
||||
Arg::new("defaultTemplate")
|
||||
.short('d')
|
||||
.action(ArgAction::SetTrue),
|
||||
),
|
||||
command_edit_action_list,
|
||||
)?;
|
||||
cli.subcommand(Command::new("nextAction").alias("ne").arg(
|
||||
Arg::new("break")
|
||||
.short('b')
|
||||
.action(ArgAction::SetTrue),
|
||||
).arg(
|
||||
Arg::new("action")
|
||||
.trailing_var_arg(true)
|
||||
.allow_hyphen_values(true)
|
||||
.num_args(0..), // Accepts 0 or more arguments
|
||||
), command_next_action)?;
|
||||
cli.subcommand(
|
||||
Command::new("printCurrentAction").alias("cu"),
|
||||
command_current_action,
|
||||
)?;
|
||||
cli.subcommand(
|
||||
Command::new("listActions").alias("la"),
|
||||
command_list_actions,
|
||||
)?;
|
||||
cli.subcommand(
|
||||
Command::new("listActivities").alias("lac"),
|
||||
command_list_activities,
|
||||
)?;
|
||||
cli.subcommand(
|
||||
Command::new("editActivities").alias("eac"),
|
||||
command_edit_activities,
|
||||
)?;
|
||||
mize.add_listener(
|
||||
"timetrack.sub_current",
|
||||
Box::new(timetrack_sub_current_listener),
|
||||
)?;
|
||||
|
||||
|
||||
}
|
||||
mize.register_part(Box::new(TimeTrack { mize: mize.clone() }))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn send_current_update_msg(conn_id: u64, data: TaskPart, mize: &mut Mize) -> MizeResult<()> {
|
||||
let now = fixed_len(data.current.name.clone(), 48);
|
||||
let next = fixed_len(
|
||||
data.upcoming
|
||||
.iter()
|
||||
.nth(0)
|
||||
.unwrap_or(&Action {
|
||||
name: "".to_string(),
|
||||
activity: None,
|
||||
task: None,
|
||||
start: None,
|
||||
end: None,
|
||||
duration: None,
|
||||
})
|
||||
.name
|
||||
.clone(),
|
||||
48,
|
||||
);
|
||||
let msg = MizeMessage::new_custom(
|
||||
"timetrack.current",
|
||||
format!(" {} NEXT: {} ", now, next),
|
||||
conn_id,
|
||||
);
|
||||
let con = mize.get_connection(conn_id)?;
|
||||
con.send(msg)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn fixed_len(mut buf: String, len: usize) -> String {
|
||||
if buf.len() > len {
|
||||
buf.truncate(len - 4); // Cut off extra characters
|
||||
buf.push_str(" ...");
|
||||
} else {
|
||||
while buf.len() < len {
|
||||
buf.push(' '); // Fill with whitespace
|
||||
}
|
||||
}
|
||||
buf
|
||||
}
|
||||
|
||||
pub fn timetrack_sub_current_listener(msg: MizeMessage, mize: &mut Mize) -> MizeResult<()> {
|
||||
let spacetime = mize.get_part_native::<SpacetimePart>("spacetime").unwrap();
|
||||
let con = spacetime.con();
|
||||
let mut mize_clone = mize.clone();
|
||||
let mut mize_clone_two = mize.clone();
|
||||
con.subscription_builder()
|
||||
.on_applied(move |ctx| {
|
||||
let data = ctx.db.task_part().identity().find(&ctx.identity()).unwrap();
|
||||
if let Err(e) = send_current_update_msg(msg.conn_id, data, &mut mize_clone) {
|
||||
e.log();
|
||||
}
|
||||
})
|
||||
.add_query(|q| q.from.task_part())
|
||||
.subscribe();
|
||||
con.db
|
||||
.task_part()
|
||||
.on_update(move |_, _old: &TaskPart, new: &TaskPart| {
|
||||
if let Err(e) = send_current_update_msg(msg.conn_id, new.clone(), &mut mize_clone_two) {
|
||||
e.log();
|
||||
}
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
impl MizePartCreate for TimeTrack {}
|
||||
|
||||
impl MizePart for TimeTrack {
|
||||
@@ -58,22 +145,45 @@ impl MizePart for TimeTrack {
|
||||
}
|
||||
}
|
||||
|
||||
fn command_next_action(_: &ArgMatches, mut mize: Mize) -> MizeResult<()> {
|
||||
#[cfg(feature = "target-os")]
|
||||
fn command_next_action(matches: &ArgMatches, mut mize: Mize) -> MizeResult<()> {
|
||||
let mut spacetime = mize.get_part_native::<SpacetimePart>("spacetime")?;
|
||||
let con = spacetime.con();
|
||||
|
||||
let break_flag = matches.get_flag("break");
|
||||
let action = matches.get_many::<String>("action")
|
||||
.map(|vals| vals.map(|s| s.as_str()).collect::<Vec<&str>>().join(" "));
|
||||
let action = match action {
|
||||
Some(action) => Some(Action {
|
||||
name: action.clone(),
|
||||
activity: None,
|
||||
task: None,
|
||||
start: None,
|
||||
end: None,
|
||||
duration: None,
|
||||
}),
|
||||
None => None,
|
||||
};
|
||||
|
||||
let old_action = get_current_action(&mut mize, con)?;
|
||||
out!("before action: {}", old_action.name);
|
||||
let (wait_tx, waiter_rx) = std::sync::oneshot::channel::<()>();
|
||||
con.reducers.next_action_then(|_, _| {
|
||||
con.reducers.next_action_then(break_flag, action, |_, _| {
|
||||
wait_tx.send(()).unwrap();
|
||||
})?;
|
||||
waiter_rx.recv()?;
|
||||
|
||||
let new_action = get_current_action(&mut mize, con)?;
|
||||
out!("new action: {}", new_action.name);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "target-os")]
|
||||
fn command_current_action(_: &ArgMatches, mut mize: Mize) -> MizeResult<()> {
|
||||
let mut spacetime = mize.get_part_native::<SpacetimePart>("spacetime")?;
|
||||
let action = get_current_action(&mut mize, spacetime.con())?;
|
||||
println!("{}", action.name);
|
||||
out!("{}", action.name);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -106,6 +216,7 @@ fn get_current_action(_mize: &mut Mize, con: &DbConnection) -> MizeResult<Action
|
||||
Ok(action)
|
||||
}
|
||||
|
||||
#[cfg(feature = "target-os")]
|
||||
fn command_list_activities(_: &ArgMatches, mut mize: Mize) -> MizeResult<()> {
|
||||
let mut spacetime = mize.get_part_native::<SpacetimePart>("spacetime")?;
|
||||
let activities = get_activities(&mut mize, spacetime.con())?;
|
||||
@@ -124,24 +235,26 @@ fn command_list_activities(_: &ArgMatches, mut mize: Mize) -> MizeResult<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "target-os")]
|
||||
fn command_list_actions(_: &ArgMatches, mut mize: Mize) -> MizeResult<()> {
|
||||
let mut spacetime = mize.get_part_native::<SpacetimePart>("spacetime")?;
|
||||
|
||||
let actions = get_actions(spacetime.con())?;
|
||||
|
||||
for action in actions {
|
||||
println!("{}", action.name)
|
||||
out!("{}", action.name)
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "target-os")]
|
||||
fn command_edit_action_list(matches: &ArgMatches, mut mize: Mize) -> MizeResult<()> {
|
||||
let mut spacetime = mize.get_part_native::<SpacetimePart>("spacetime")?;
|
||||
|
||||
// maybe append a template to the action list
|
||||
if matches.get_flag("defaultTemplate") {
|
||||
println!("Appending template...");
|
||||
out!("Appending template...");
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
@@ -158,10 +271,7 @@ fn command_edit_action_list(matches: &ArgMatches, mut mize: Mize) -> MizeResult<
|
||||
.map_err(|e| mize_err!("Failed to write temp file: {}", e))?;
|
||||
|
||||
// open $EDITOR blocking
|
||||
std::process::Command::new("nvim")
|
||||
.arg(&tmp_file)
|
||||
.status()
|
||||
.map_err(|e| mize_err!("Failed to run nvim: {}", e))?;
|
||||
crate::cli_spawn(format!("nvim {}", tmp_file.display()))?;
|
||||
|
||||
// read file and update db
|
||||
let new_file_content = std::fs::read_to_string(&tmp_file)
|
||||
@@ -239,6 +349,7 @@ fn parse_action(action: &mut Action) {
|
||||
let _parts: Vec<&str> = action.name.split_whitespace().collect();
|
||||
}
|
||||
|
||||
#[cfg(feature = "target-os")]
|
||||
fn command_edit_activities(_: &ArgMatches, mut mize: Mize) -> MizeResult<()> {
|
||||
let mut spacetime = mize.get_part_native::<SpacetimePart>("spacetime")?;
|
||||
|
||||
@@ -270,10 +381,7 @@ fn command_edit_activities(_: &ArgMatches, mut mize: Mize) -> MizeResult<()> {
|
||||
|
||||
loop {
|
||||
// open $EDITOR blocking
|
||||
std::process::Command::new("nvim")
|
||||
.arg(&tmp_file)
|
||||
.status()
|
||||
.map_err(|e| mize_err!("Failed to run nvim: {}", e))?;
|
||||
crate::cli_spawn(format!("nvim {}", tmp_file.display()))?;
|
||||
|
||||
let new_file_content = std::fs::read_to_string(&tmp_file)
|
||||
.map_err(|e| mize_err!("Failed to read temp file: {}", e))?;
|
||||
@@ -284,8 +392,8 @@ fn command_edit_activities(_: &ArgMatches, mut mize: Mize) -> MizeResult<()> {
|
||||
if let Err(err) =
|
||||
parse_activities_from_text(&mut mize, con, new_file_content, old_activities)
|
||||
{
|
||||
println!("error parsing the activities from the file: {:?}", err);
|
||||
println!("press ENTER to open the file again");
|
||||
out!("error parsing the activities from the file: {:?}", err);
|
||||
out!("press ENTER to open the file again");
|
||||
|
||||
// wait for user input
|
||||
// so that they can se the err msg, before running nvim again
|
||||
@@ -301,7 +409,7 @@ fn command_edit_activities(_: &ArgMatches, mut mize: Mize) -> MizeResult<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn get_activities(_mize: &mut Mize, con: &mut DbConnection) -> MizeResult<Vec<Activity>> {
|
||||
fn get_activities(_mize: &mut Mize, con: &DbConnection) -> MizeResult<Vec<Activity>> {
|
||||
let (exit_tx, exit_rx) = std::sync::oneshot::channel::<Vec<Activity>>();
|
||||
con.subscription_builder()
|
||||
.on_applied(|ctx| {
|
||||
@@ -315,7 +423,7 @@ fn get_activities(_mize: &mut Mize, con: &mut DbConnection) -> MizeResult<Vec<Ac
|
||||
|
||||
fn parse_activities_from_text(
|
||||
mize: &mut Mize,
|
||||
con: &mut DbConnection,
|
||||
con: &DbConnection,
|
||||
text: String,
|
||||
old_activities: Vec<Activity>,
|
||||
) -> MizeResult<()> {
|
||||
@@ -385,7 +493,7 @@ fn parse_activities_from_text(
|
||||
|
||||
// check for declared parents
|
||||
let new_activities = get_activities(mize, con)?;
|
||||
println!("new_activities: {:?}", new_activities);
|
||||
out!("new_activities: {:?}", new_activities);
|
||||
for line in text.lines() {
|
||||
if line == "" {
|
||||
continue;
|
||||
@@ -21,9 +21,9 @@ required-features = [ "target-os" ]
|
||||
|
||||
[features]
|
||||
default = [ "target-os" ]
|
||||
target-os= [ "clap", "home", "nix", "sysinfo", "ciborium/default", "tracing-subscriber", "async", "tracing-subscriber/env-filter", "libloading", "ciborium-io", "tracing-core", "tar", "flate2", "http_req" ]
|
||||
target-js = [ "wasm-bindgen", "console_error_panic_hook", "web-sys", "web-sys/Worker", "web-sys/Window", "web-sys/Request", "web-sys/RequestInit", "web-sys/RequestMode", "web-sys/Response", "web-sys/WorkerOptions", "web-sys/WorkerType", "serde-wasm-bindgen", "wasm-bindgen-futures", "wee_alloc"]
|
||||
async = ["tokio/net", "tokio", "tokio/rt-multi-thread", "tokio/io-util"]
|
||||
target-os = [ "clap", "home", "nix", "sysinfo", "ciborium/default", "tracing-subscriber", "async", "tracing-subscriber/env-filter", "libloading", "ciborium-io", "tracing-core", "tar", "flate2", "http_req", "tokio/net", "tokio", "tokio/rt-multi-thread", "tokio/io-util" ]
|
||||
target-js = [ "wasm-bindgen", "console_error_panic_hook", "web-sys", "web-sys/Worker", "web-sys/Window", "web-sys/Request", "web-sys/RequestInit", "web-sys/RequestMode", "web-sys/Response", "web-sys/WorkerOptions", "web-sys/WorkerType", "serde-wasm-bindgen", "wasm-bindgen-futures", "wee_alloc", "ciborium/default" ]
|
||||
async = [ "tokio", ]
|
||||
|
||||
|
||||
[dependencies]
|
||||
@@ -38,13 +38,10 @@ serde_json = { version = "1.0.117", optional = false }
|
||||
toml = { version = "0.8.13", features = ["parse"], optional = false }
|
||||
dyn-clone = "1.0.17"
|
||||
sha2 = "0.10.8"
|
||||
flume = "0.11.1"
|
||||
mize_macros = { path = "../ac_mize_macros" }
|
||||
flume = { version = "0.11.1", default-features = false, features = [ "async" ] }
|
||||
mize_macros = { path = "../macros" }
|
||||
async-trait = "0.1.89"
|
||||
|
||||
# builtin modules
|
||||
#mize_module_blob = { path = "./modules/modules/Blob" }
|
||||
|
||||
# wasm dependencies
|
||||
wasm-bindgen = { version = "0.2.84", optional = true }
|
||||
console_error_panic_hook = { version = "0.1.7", optional = true }
|
||||
@@ -60,7 +57,7 @@ daemonize = { version = "0.5.0", optional = true }
|
||||
nix = { version = "0.27.1", features = ["signal"], optional = true }
|
||||
sysinfo = { version = "0.30.12", optional = true }
|
||||
tracing-subscriber = { version = "0.3.18", optional = true }
|
||||
tokio = { version = "1.46.1", features = ["sync"], optional = true }
|
||||
tokio = { version = "1.46.1", features = ["sync", "rt" ], optional = true, default-features = false }
|
||||
libloading = { version = "0.8.5", optional = true }
|
||||
ciborium-io = { version = "0.2.2", optional = true }
|
||||
tracing-core = { version = "0.1.32", optional = true }
|
||||
@@ -95,3 +92,8 @@ wasm-bindgen-test = { version = "0.3.34" }
|
||||
[profile.release]
|
||||
# Tell `rustc` to optimize for small code size.
|
||||
opt-level = "s"
|
||||
|
||||
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
||||
# to make uuid be able to use browser apis for generating random numbers
|
||||
# needs to be the same version as uuid uses
|
||||
getrandom = { version = "0.4.3", features = ["wasm_js"] }
|
||||
@@ -1,4 +1,6 @@
|
||||
use colored::Colorize;
|
||||
#[cfg(feature = "target-js")]
|
||||
use wasm_bindgen::JsValue;
|
||||
use core::result::Result;
|
||||
use core::result::Result::Ok;
|
||||
use std::collections::HashMap;
|
||||
@@ -24,8 +26,8 @@ pub trait MizeResultTrait<T> {
|
||||
}
|
||||
|
||||
pub trait IntoMizeResult<T, S> {
|
||||
fn mize_result(self) -> MizeResult<T>;
|
||||
fn mize_result_msg(self, msg: S) -> MizeResult<T>
|
||||
fn mres(self) -> MizeResult<T>;
|
||||
fn mres_msg(self, msg: S) -> MizeResult<T>
|
||||
where
|
||||
S: std::fmt::Display;
|
||||
}
|
||||
@@ -125,16 +127,16 @@ impl MizeError {
|
||||
}
|
||||
|
||||
pub fn log(self) -> MizeError {
|
||||
error!("MizeError encountered!");
|
||||
println!("[ {} ] MizeError encountered!", "ERROR".red());
|
||||
|
||||
if let Some(ref location) = self.code_location {
|
||||
error!("[ {} ] {}", "LOCATION".yellow(), location);
|
||||
println!("[ {} ] {}", "LOCATION".yellow(), location);
|
||||
};
|
||||
|
||||
error!("[ {} ] {}", "BACKTRACE".yellow(), self.backtrace);
|
||||
println!("[ {} ] {}", "BACKTRACE".yellow(), self.backtrace);
|
||||
|
||||
for msg in &self.messages {
|
||||
error!("[ {} ] {}", "MSG".yellow(), msg);
|
||||
println!("[ {} ] {}", "MSG".yellow(), msg);
|
||||
}
|
||||
|
||||
self
|
||||
@@ -261,7 +263,7 @@ where
|
||||
E: std::fmt::Display,
|
||||
{
|
||||
#[track_caller]
|
||||
fn mize_result(self) -> MizeResult<T> {
|
||||
fn mres(self) -> MizeResult<T> {
|
||||
let caller_location = std::panic::Location::caller();
|
||||
|
||||
match self {
|
||||
@@ -274,7 +276,7 @@ where
|
||||
}
|
||||
}
|
||||
#[track_caller]
|
||||
fn mize_result_msg(self, msg: S) -> MizeResult<T>
|
||||
fn mres_msg(self, msg: S) -> MizeResult<T>
|
||||
where
|
||||
S: std::fmt::Display,
|
||||
{
|
||||
@@ -346,3 +348,20 @@ impl From<MizeError> for MizeErrorWhichImplementsError {
|
||||
}
|
||||
|
||||
impl std::error::Error for MizeErrorWhichImplementsError {}
|
||||
|
||||
// i hate rust sometimes.
|
||||
// why can't i impl IntoMizeResult for any type. and the extra concrete impl of it for JsValue overrides the more generic one.
|
||||
#[cfg(feature = "target-js")]
|
||||
pub trait IntoMizeResultJsValue<T> {
|
||||
fn mres(self) -> MizeResult<T>;
|
||||
}
|
||||
|
||||
#[cfg(feature = "target-js")]
|
||||
impl<T> IntoMizeResultJsValue<T> for Result<T, JsValue> {
|
||||
fn mres(self) -> MizeResult<T> {
|
||||
match self {
|
||||
Ok(value) => Ok(value),
|
||||
Err(err) => Err(mize_err!("From JsValue: {:?}", err)),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,7 @@ use std::fs::File;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::thread::panicking;
|
||||
use std::{thread, vec};
|
||||
use tracing::{debug, error, info, trace, warn, Instrument};
|
||||
use uuid::Uuid;
|
||||
@@ -63,7 +64,11 @@ pub struct Mize {
|
||||
pub(crate) id_pool: Arc<Mutex<VecStringPool>>,
|
||||
pub(crate) namespace_pool: Arc<Mutex<StringPool>>,
|
||||
|
||||
pub(crate) parts: Arc<Mutex<HashMap<&'static str, Option<Box<dyn MizePart + Sync + Send>>>>>,
|
||||
pub(crate) listeners: Arc<
|
||||
Mutex<HashMap<String, Arc<dyn Fn(MizeMessage, &mut Mize) -> MizeResult<()> + Send + Sync>>>,
|
||||
>,
|
||||
|
||||
pub parts: Arc<Mutex<HashMap<&'static str, Option<Box<dyn MizePart + Sync + Send>>>>>,
|
||||
//pub(crate) platform: Arc<Mutex<Box<dyn MizePlatform>>>, // should we do that instead of all the #cfg(faturs=targt-smth) in lib.rs
|
||||
part_names: Arc<Mutex<Vec<&'static str>>>, // exists, so that we know the ordering in which parts were loaded
|
||||
|
||||
@@ -256,6 +261,7 @@ impl Mize {
|
||||
parts: Arc::new(Mutex::new(HashMap::new())),
|
||||
part_names: Arc::new(Mutex::new(Vec::new())),
|
||||
config_opts: Arc::new(Mutex::new(HashMap::new())),
|
||||
listeners: Arc::new(Mutex::new(HashMap::new())),
|
||||
connections,
|
||||
subs,
|
||||
id_pool,
|
||||
@@ -270,8 +276,9 @@ impl Mize {
|
||||
create_msg_wait,
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
#[cfg(not(feature = "target-js"))]
|
||||
runtime: Arc::new(Mutex::new(
|
||||
Runtime::new().mize_result_msg("Could not create async runtime")?,
|
||||
Runtime::new().mres_msg("Could not create async runtime")?,
|
||||
)),
|
||||
};
|
||||
|
||||
@@ -354,6 +361,18 @@ impl Mize {
|
||||
self.parts.lock().unwrap().insert(part.name(), Some(part));
|
||||
Ok(())
|
||||
}
|
||||
pub fn add_listener(
|
||||
&mut self,
|
||||
name: &str,
|
||||
handler: Box<dyn Fn(MizeMessage, &mut Mize) -> MizeResult<()> + Send + Sync>,
|
||||
) -> MizeResult<()> {
|
||||
println!("adding listener: {}", name);
|
||||
let name = name.to_owned();
|
||||
let mut listeners = self.listeners.lock().unwrap();
|
||||
listeners.insert(name, Arc::new(handler));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
fn part_names(&mut self) -> Vec<&'static str> {
|
||||
self.part_names.lock().unwrap().clone()
|
||||
}
|
||||
@@ -372,9 +391,16 @@ impl Mize {
|
||||
// end of platform specific init code
|
||||
|
||||
// load the modules, as specified in the parts config
|
||||
match self.get_config("parts")?.value_string() {
|
||||
match self
|
||||
.get_config("parts")
|
||||
.unwrap_or(ItemData::new())
|
||||
.value_string()
|
||||
{
|
||||
Ok(parts_to_load) => {
|
||||
for part_name in parts_to_load.split(" ") {
|
||||
if part_name.trim().is_empty() {
|
||||
continue;
|
||||
}
|
||||
self.load_part(part_name)?;
|
||||
}
|
||||
}
|
||||
@@ -390,14 +416,35 @@ impl Mize {
|
||||
pub fn with_config(config: ItemData) -> MizeResult<Mize> {
|
||||
trace!("[ {} ] Instance::with_config()", "CALL".yellow());
|
||||
trace!("config: {}", config);
|
||||
println!("here ....");
|
||||
|
||||
let mut instance = Mize::empty()?;
|
||||
let mut mize = Mize::empty()?;
|
||||
|
||||
instance.set_blocking("0", config);
|
||||
let mut opts = mize.config_opts.lock().unwrap();
|
||||
let mut config = config.clone();
|
||||
for mut path in config.get_paths_recursive()? {
|
||||
if path.starts_with("/") {
|
||||
path.remove(0);
|
||||
}
|
||||
let path_split = path
|
||||
.split("/")
|
||||
.map(|s| s.to_owned())
|
||||
.collect::<Vec<String>>();
|
||||
let path_string = path_split.join(".");
|
||||
if let Ok(val) = config.get_path(path_split) {
|
||||
let opt = ConfigOpt {
|
||||
name: path.clone(),
|
||||
val: Some(val),
|
||||
thunk: None,
|
||||
};
|
||||
opts.insert(path_string, opt);
|
||||
}
|
||||
}
|
||||
drop(opts);
|
||||
|
||||
instance.init()?;
|
||||
mize.init()?;
|
||||
|
||||
Ok(instance)
|
||||
Ok(mize)
|
||||
}
|
||||
|
||||
pub fn migrate_to_store(&self, new_store: Box<dyn Store>) -> MizeResult<()> {
|
||||
@@ -587,7 +634,7 @@ impl Mize {
|
||||
Ok(self.get_namespace()? == self.get_self_namespace()?)
|
||||
}
|
||||
|
||||
pub fn add_listener<T: ConnListener + 'static>(&mut self, listener: T) -> MizeResult<()> {
|
||||
pub fn add_conn_listener<T: ConnListener + 'static>(&mut self, listener: T) -> MizeResult<()> {
|
||||
let mut instance_clone = self.clone();
|
||||
self.spawn_background("some_listener", move || listener.listen(instance_clone));
|
||||
Ok(())
|
||||
@@ -637,6 +684,21 @@ impl Mize {
|
||||
}
|
||||
|
||||
pub fn report_err(&self, err: MizeError) {
|
||||
#[cfg(feature = "target-js")] unsafe {
|
||||
use wasm_bindgen::prelude::wasm_bindgen;
|
||||
#[wasm_bindgen]
|
||||
extern "C" {
|
||||
#[wasm_bindgen(js_namespace = console)]
|
||||
fn log(s: &str);
|
||||
}
|
||||
|
||||
macro_rules! console_log {
|
||||
// Note that this is using the `log` function imported above during
|
||||
// `bare_bones`
|
||||
($($t:tt)*) => ({ log(&format_args!($($t)*).to_string())})
|
||||
}
|
||||
console_log!("reported_err: {:?}", err);
|
||||
}
|
||||
err.log();
|
||||
}
|
||||
|
||||
@@ -751,13 +813,16 @@ impl Mize {
|
||||
let my_thread_id_no_mutex_guard = *next_thread_id;
|
||||
let thread_mutex = self.threads.clone();
|
||||
let name_to_move = name.to_owned();
|
||||
let to_spawn = move || -> MizeResult<()> {
|
||||
let to_spawn = move || -> () {
|
||||
debug!("spawning thread: {}", name_to_move);
|
||||
let my_thread_id = my_thread_id_no_mutex_guard;
|
||||
|
||||
func()?;
|
||||
let res = func();
|
||||
if let Err(e) = res {
|
||||
e.log();
|
||||
}
|
||||
|
||||
let mut threads_inner = thread_mutex.lock()?;
|
||||
//let mut threads_inner = thread_mutex.lock()?;
|
||||
/*
|
||||
*threads_inner = threads_inner
|
||||
.clone()
|
||||
@@ -769,7 +834,6 @@ impl Mize {
|
||||
.collect();
|
||||
*/
|
||||
debug!("thread '{}' stopped", name_to_move);
|
||||
Ok(())
|
||||
};
|
||||
|
||||
*next_thread_id += 1;
|
||||
@@ -948,7 +1012,10 @@ impl Mize {
|
||||
let mut dyn_guard = self.get_part(name)?;
|
||||
let part = dyn_guard.part.take().unwrap();
|
||||
drop(dyn_guard);
|
||||
let concrete_part = part.into_any().downcast::<T>().unwrap();
|
||||
let concrete_part = part
|
||||
.into_any()
|
||||
.downcast::<T>()
|
||||
.map_err(|_| mize_err!("failed to downcast"))?;
|
||||
Ok(MizePartGuard {
|
||||
mize: self.clone(),
|
||||
part: Some(*concrete_part),
|
||||
@@ -215,6 +215,19 @@ fn handle_msg(msg: &mut MizeMessage, instance: &Mize) -> MizeResult<()> {
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
MessageCmd::Custom(type_val) => {
|
||||
let listeners = instance.listeners.lock()?;
|
||||
let type_str = type_val.value_string()?;
|
||||
let msg = msg.clone();
|
||||
let mut mize = instance.clone();
|
||||
println!("type_str: {}", type_str);
|
||||
println!("listeners: {:?}", listeners.keys());
|
||||
if let Some(listener) = listeners.get(&type_str) {
|
||||
let listener = listener.clone();
|
||||
drop(listeners);
|
||||
listener(msg, &mut mize)?;
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
return Err(mize_err!("got a message, that is not handeled"));
|
||||
}
|
||||
@@ -50,7 +50,7 @@ impl Item<'_> {
|
||||
pub fn value_string(&self) -> MizeResult<String> {
|
||||
let raw = self.value_raw()?;
|
||||
String::from_utf8(raw.clone())
|
||||
.mize_result_msg(format!("failed to convert raw value from item '{}' into a utf-8 string. raw value was: '{:02X?}'", self.id(), raw))
|
||||
.mres_msg(format!("failed to convert raw value from item '{}' into a utf-8 string. raw value was: '{:02X?}'", self.id(), raw))
|
||||
}
|
||||
|
||||
pub fn as_data_full(&self) -> MizeResult<ItemData> {
|
||||
@@ -193,7 +193,7 @@ impl ItemData {
|
||||
pub fn from_toml(toml_string: &str) -> MizeResult<ItemData> {
|
||||
let toml_deserializer = toml::Deserializer::new(&toml_string);
|
||||
|
||||
let value = CborValue::deserialize(toml_deserializer).mize_result_msg(format!(
|
||||
let value = CborValue::deserialize(toml_deserializer).mres_msg(format!(
|
||||
"Could not deserialize the toml string: {}",
|
||||
&toml_string
|
||||
))?;
|
||||
@@ -246,7 +246,7 @@ impl ItemData {
|
||||
pub fn from_json(json_str: String) -> MizeResult<ItemData> {
|
||||
let mut json_deserializer = serde_json::Deserializer::from_str(json_str.as_str());
|
||||
|
||||
let value = CborValue::deserialize(&mut json_deserializer).mize_result_msg(format!(
|
||||
let value = CborValue::deserialize(&mut json_deserializer).mres_msg(format!(
|
||||
"could not deserialize the json string: {}",
|
||||
json_str
|
||||
))?;
|
||||
@@ -682,6 +682,9 @@ pub trait IntoItemData {
|
||||
|
||||
impl IntoPath for &str {
|
||||
fn into_path(self) -> Vec<String> {
|
||||
if !self.contains('/') {
|
||||
return self.split(".").map(|v| v.to_owned()).collect();
|
||||
}
|
||||
self.to_owned().split("/").map(|v| v.to_owned()).collect()
|
||||
}
|
||||
}
|
||||
@@ -704,7 +707,7 @@ impl IntoPath for Vec<&str> {
|
||||
|
||||
impl IntoItemData for &str {
|
||||
fn into_item_data(self) -> ItemData {
|
||||
ItemData::parse(self)
|
||||
ItemData(self.into())
|
||||
}
|
||||
}
|
||||
impl IntoItemData for String {
|
||||
@@ -717,6 +720,15 @@ impl IntoItemData for &String {
|
||||
ItemData(self.to_owned().into())
|
||||
}
|
||||
}
|
||||
impl IntoItemData for Vec<String> {
|
||||
fn into_item_data(self) -> ItemData {
|
||||
let array = self
|
||||
.into_iter()
|
||||
.map(|v| v.into_item_data().cbor().to_owned())
|
||||
.collect::<Vec<_>>();
|
||||
ItemData(CborValue::Array(array))
|
||||
}
|
||||
}
|
||||
impl IntoItemData for CborValue {
|
||||
fn into_item_data(self) -> ItemData {
|
||||
ItemData(self)
|
||||
@@ -732,6 +744,11 @@ impl IntoItemData for i128 {
|
||||
ItemData(self.into())
|
||||
}
|
||||
}
|
||||
impl IntoItemData for bool {
|
||||
fn into_item_data(self) -> ItemData {
|
||||
ItemData(self.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for ItemData {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
@@ -145,7 +145,7 @@ fn id_to_u64(id: MizeId) -> MizeResult<u64> {
|
||||
}
|
||||
|
||||
fn str_to_u64(id_str: &str) -> MizeResult<u64> {
|
||||
id_str.parse().mize_result_msg(format!(
|
||||
id_str.parse().mres_msg(format!(
|
||||
"Could not parse the store_part of mizeid {} into a u64 for the MemStore",
|
||||
id_str
|
||||
))
|
||||
@@ -17,6 +17,17 @@ pub struct MizeMessage {
|
||||
pub conn_id: u64,
|
||||
}
|
||||
|
||||
impl MizeMessage {
|
||||
pub fn bytes(&self) -> Vec<u8> {
|
||||
let mut writer = Vec::new();
|
||||
ciborium::into_writer(&self.value, &mut writer).unwrap();
|
||||
writer
|
||||
}
|
||||
pub fn into_writer(&self, writer: &mut impl io::Write) -> MizeResult<()> {
|
||||
Ok(ciborium::into_writer(&self.value, writer)?)
|
||||
}
|
||||
}
|
||||
|
||||
// msg items
|
||||
static MSG_CMD: u16 = 1;
|
||||
static MSG_ID: u16 = 2;
|
||||
@@ -42,6 +53,7 @@ pub enum MessageCmd {
|
||||
UpdateRequest,
|
||||
GetSub,
|
||||
Sub,
|
||||
Custom(ItemData),
|
||||
}
|
||||
|
||||
impl MizeMessage {
|
||||
@@ -49,6 +61,24 @@ impl MizeMessage {
|
||||
MizeMessage { value, conn_id }
|
||||
}
|
||||
|
||||
pub fn new_custom<T: IntoItemData, U: IntoItemData>(
|
||||
custom_cmd: T,
|
||||
value: U,
|
||||
conn_id: u64,
|
||||
) -> MizeMessage {
|
||||
let value = value.into_item_data();
|
||||
let custom_cmd = custom_cmd.into_item_data();
|
||||
|
||||
let cmd = (CborValue::Integer(MSG_CMD.into()), custom_cmd.0);
|
||||
let data = (CborValue::Integer(MSG_DATA.into()), value.cbor().to_owned());
|
||||
let msg_value = CborValue::Map(vec![cmd, data]);
|
||||
|
||||
MizeMessage {
|
||||
value: msg_value,
|
||||
conn_id,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_get(id: MizeId, conn_id: u64) -> MizeMessage {
|
||||
let id_path = id
|
||||
.path()
|
||||
@@ -194,7 +224,7 @@ impl MizeMessage {
|
||||
}
|
||||
};
|
||||
|
||||
//check if in this map there is a c, otherwise return err
|
||||
//check if in this map there is a cmd, otherwise return err
|
||||
let val_of_c = {
|
||||
let mut tmp_val = 0;
|
||||
for (key, val) in msg_as_map {
|
||||
@@ -204,6 +234,8 @@ impl MizeMessage {
|
||||
if key_int == &one {
|
||||
if let CborValue::Integer(val_int) = val {
|
||||
tmp_val = val_int.to_owned().into();
|
||||
} else {
|
||||
return Ok(MessageCmd::Custom(ItemData(val.to_owned())));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -214,7 +246,7 @@ impl MizeMessage {
|
||||
tmp_val
|
||||
};
|
||||
|
||||
// match on value of c
|
||||
// match on value of cmd
|
||||
let cmd = match val_of_c {
|
||||
1 => MessageCmd::Get,
|
||||
2 => MessageCmd::Update,
|
||||
@@ -380,7 +412,7 @@ impl fmt::Display for MizeMessage {
|
||||
self.value
|
||||
.serialize(&mut serde_json::Serializer::pretty(display_writer))
|
||||
.map_err(|serde_err| std::fmt::Error)
|
||||
.mize_result_msg("serialize error")
|
||||
.mres_msg("serialize error")
|
||||
.inspect_err(|e| {
|
||||
e.clone().log();
|
||||
});
|
||||
@@ -2,6 +2,54 @@
|
||||
|
||||
static PROTO_VERSION: u8 = 1;
|
||||
|
||||
use std::cell::RefCell;
|
||||
use std::fmt::format;
|
||||
|
||||
thread_local! {
|
||||
pub static MIZE_STDOUT: RefCell<Option<Box<dyn Fn(String) -> MizeResult<()>>>> = const { RefCell::new(None) };
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! out {
|
||||
($($arg:tt)*) => {
|
||||
// format_args! handles string interpolation safely at compile time
|
||||
if let Err(e) = $crate::write_to_stdout(format_args!($($arg)*)) {
|
||||
// Choose your error handling behavior here (panic, print to stderr, or ignore)
|
||||
eprintln!("out! macro failed to write!!");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub fn out_macro_stdout() -> MizeResult<()> {
|
||||
MIZE_STDOUT.with(|cell| {
|
||||
*cell.borrow_mut() = Some(Box::new(|s| {
|
||||
println!("{}", s);
|
||||
Ok(())
|
||||
}));
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn write_to_stdout(args: std::fmt::Arguments) -> MizeResult<()> {
|
||||
MIZE_STDOUT.with(|stdout| {
|
||||
if let Some(fun) = stdout.borrow().as_ref() {
|
||||
fun(format!("{}", args));
|
||||
Ok(())
|
||||
} else {
|
||||
Err(std::io::Error::new(
|
||||
std::io::ErrorKind::Other,
|
||||
"stdout not initialized",
|
||||
))
|
||||
}
|
||||
})?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub mod prelude {
|
||||
pub use super::*;
|
||||
}
|
||||
pub use flume;
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! test_println {
|
||||
($($arg:tt)*) => {
|
||||
@@ -43,6 +91,7 @@ mod core {
|
||||
}
|
||||
|
||||
pub use async_trait::async_trait;
|
||||
pub use ciborium;
|
||||
pub use core::error::MizeError;
|
||||
pub use core::error::MizeResult;
|
||||
pub use core::instance::Mize;
|
||||
@@ -78,7 +127,8 @@ pub mod platform {
|
||||
pub use super::os::load_part;
|
||||
|
||||
#[cfg(feature = "target-js")]
|
||||
pub use super::js::load_part;
|
||||
//pub use super::js::load_part;
|
||||
pub use super::super::load_part;
|
||||
|
||||
#[cfg(not(any(feature = "target-os", feature = "target-js")))]
|
||||
pub use super::super::load_part;
|
||||
@@ -11,10 +11,9 @@ use web_sys::{WorkerOptions, WorkerType};
|
||||
|
||||
use crate::core::item::IntoItemData;
|
||||
use crate::error::MizeResult;
|
||||
use crate::instance::module::EmptyModule;
|
||||
use crate::Mize;
|
||||
use crate::MizeError;
|
||||
use crate::{mize_err, Module};
|
||||
use crate::mize_err;
|
||||
|
||||
// console_log macro
|
||||
// that can be copied into other files for debugging purposes
|
||||
@@ -53,13 +52,14 @@ pub fn wasm_instance_init(mize: &mut Mize) -> MizeResult<()> {
|
||||
}
|
||||
|
||||
pub fn load_module(mize: &mut Mize, name: &str, path: Option<String>) -> MizeResult<()> {
|
||||
console_log!("loading module: {}", name);
|
||||
console_log!("loading module NOT IMPLEMENTED: {}", name);
|
||||
|
||||
wasm_bindgen_futures::spawn_local(load_module_async(mize.clone(), name.to_owned(), path));
|
||||
//wasm_bindgen_futures::spawn_local(load_module_async(mize.clone(), name.to_owned(), path));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/*
|
||||
async fn load_module_async(mut mize: Mize, name: String, path: Option<String>) -> () {
|
||||
let name = name.as_str();
|
||||
let module_url = fetch_module(&mut mize, name).unwrap();
|
||||
@@ -145,8 +145,9 @@ async fn load_module_async(mut mize: Mize, name: String, path: Option<String>) -
|
||||
}
|
||||
*/
|
||||
}
|
||||
*/
|
||||
|
||||
pub fn fetch_module(mize: &mut Mize, name: &str) -> MizeResult<String> {
|
||||
pub fn fetch_module(mize: &mut Mize, name: &str) -> MizeResult<String> {
|
||||
let module_name_with_slashes = name.replace(".", "/");
|
||||
if let Ok(module_path) = mize
|
||||
.get(format!(
|
||||
@@ -191,6 +192,7 @@ async fn download_module(mize: &mut Mize, name: &str) -> MizeResult<js_sys::Arra
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
#[derive(Clone)]
|
||||
pub struct JsMize {
|
||||
inner: NonNull<Mize>,
|
||||
}
|
||||
@@ -207,10 +209,7 @@ pub async fn new_js_instance(config_json_str: String) -> JsMize {
|
||||
|
||||
let config = ItemData::from_json(config_json_str).expect("parsing of json config failed");
|
||||
|
||||
let mut mize = Mize::empty().expect("Mize::empty() failed");
|
||||
|
||||
mize.set_blocking("0/config", config)
|
||||
.expect("Failed to set the config at item 0");
|
||||
let mut mize = Mize::with_config(config).expect("Mize::new() failed");
|
||||
|
||||
let mut js_instance = JsMize {
|
||||
inner: NonNull::from(Box::leak(Box::new(mize))),
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user