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
|
## 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
|
## 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.27.3",
|
||||||
"npm:esbuild@~0.25.5": "0.25.12",
|
"npm:esbuild@~0.25.5": "0.25.12",
|
||||||
"npm:esbuild@~0.27.3": "0.27.3",
|
"npm:esbuild@~0.27.3": "0.27.3",
|
||||||
|
"npm:matrix-js-sdk@42": "42.0.0",
|
||||||
"npm:mdast@3": "3.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.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",
|
"npm:obsidian@^1.12.3": "1.12.3_@codemirror+state@6.5.0_@codemirror+view@6.38.6",
|
||||||
@@ -45,6 +46,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"npm": {
|
"npm": {
|
||||||
|
"@babel/runtime@8.0.0": {
|
||||||
|
"integrity": "sha512-sL6cvO2IfkSu/iU+zs2S/w01B7A8V7suXSIKEN4hPFFdZoiPGxrj5pAG0lCaqLWiEIrjKzdznIWuaLcxPR53qw=="
|
||||||
|
},
|
||||||
"@codemirror/state@6.5.0": {
|
"@codemirror/state@6.5.0": {
|
||||||
"integrity": "sha512-MwBHVK60IiIHDcoMet78lxt6iw5gJOGSbNbOIVBHWVXIH4/Nq1+GQgLLGgI1KlnN86WDXsPudVaqYHKBIx7Eyw==",
|
"integrity": "sha512-MwBHVK60IiIHDcoMet78lxt6iw5gJOGSbNbOIVBHWVXIH4/Nq1+GQgLLGgI1KlnN86WDXsPudVaqYHKBIx7Eyw==",
|
||||||
"dependencies": [
|
"dependencies": [
|
||||||
@@ -332,6 +336,9 @@
|
|||||||
"@marijn/find-cluster-break@1.0.2": {
|
"@marijn/find-cluster-break@1.0.2": {
|
||||||
"integrity": "sha512-l0h88YhZFyKdXIFNfSWpyjStDjGHwZ/U7iobcK1cQQD8sejsONdQtTVU+1wVN1PBw40PiiHB1vA5S7VTfQiP9g=="
|
"integrity": "sha512-l0h88YhZFyKdXIFNfSWpyjStDjGHwZ/U7iobcK1cQQD8sejsONdQtTVU+1wVN1PBw40PiiHB1vA5S7VTfQiP9g=="
|
||||||
},
|
},
|
||||||
|
"@matrix-org/matrix-sdk-crypto-wasm@18.3.1": {
|
||||||
|
"integrity": "sha512-VRjWhE1UgHnPpJ3b9B5+8z71ZC/HICFngPPFIN6ktzmUBKI5RusPujzbAQUoB3CgZ0yU58L99AfSQS4YTztSWw=="
|
||||||
|
},
|
||||||
"@types/codemirror@5.60.8": {
|
"@types/codemirror@5.60.8": {
|
||||||
"integrity": "sha512-VjFgDF/eB+Aklcy15TtOTLQeMjTo07k7KAjql8OK5Dirr7a6sJY4T1uVBDuTVG9VEmn1uUsohOpYnVfgC6/jyw==",
|
"integrity": "sha512-VjFgDF/eB+Aklcy15TtOTLQeMjTo07k7KAjql8OK5Dirr7a6sJY4T1uVBDuTVG9VEmn1uUsohOpYnVfgC6/jyw==",
|
||||||
"dependencies": [
|
"dependencies": [
|
||||||
@@ -347,6 +354,9 @@
|
|||||||
"@types/estree@1.0.8": {
|
"@types/estree@1.0.8": {
|
||||||
"integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="
|
"integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="
|
||||||
},
|
},
|
||||||
|
"@types/events@3.0.3": {
|
||||||
|
"integrity": "sha512-trOc4AAUThEz9hapPtSd7wf5tiQKvTtu5b371UxXdTuqzIh0ArcRspRP0i0Viu+LXstIQ1z96t1nsPxT9ol01g=="
|
||||||
|
},
|
||||||
"@types/mdast@4.0.4": {
|
"@types/mdast@4.0.4": {
|
||||||
"integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==",
|
"integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==",
|
||||||
"dependencies": [
|
"dependencies": [
|
||||||
@@ -365,9 +375,21 @@
|
|||||||
"@types/unist@3.0.3": {
|
"@types/unist@3.0.3": {
|
||||||
"integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q=="
|
"integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q=="
|
||||||
},
|
},
|
||||||
|
"another-json@0.2.0": {
|
||||||
|
"integrity": "sha512-/Ndrl68UQLhnCdsAzEXLMFuOR546o2qbYRqCglaNHbjXrwG1ayTcdwr3zkSGOGtGXDyR5X9nCFfnyG2AFJIsqg=="
|
||||||
|
},
|
||||||
"bail@2.0.2": {
|
"bail@2.0.2": {
|
||||||
"integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw=="
|
"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": {
|
"builtin-modules@5.0.0": {
|
||||||
"integrity": "sha512-bkXY9WsVpY7CvMhKSR6pZilZu9Ln5WDrKVBUXf2S443etkmEO4V58heTecXcUIsNsi4Rx8JUO4NfX1IcQl4deg=="
|
"integrity": "sha512-bkXY9WsVpY7CvMhKSR6pZilZu9Ln5WDrKVBUXf2S443etkmEO4V58heTecXcUIsNsi4Rx8JUO4NfX1IcQl4deg=="
|
||||||
},
|
},
|
||||||
@@ -377,6 +399,9 @@
|
|||||||
"character-entities@2.0.2": {
|
"character-entities@2.0.2": {
|
||||||
"integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ=="
|
"integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ=="
|
||||||
},
|
},
|
||||||
|
"content-type@2.0.0": {
|
||||||
|
"integrity": "sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ=="
|
||||||
|
},
|
||||||
"crelt@1.0.6": {
|
"crelt@1.0.6": {
|
||||||
"integrity": "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g=="
|
"integrity": "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g=="
|
||||||
},
|
},
|
||||||
@@ -470,6 +495,9 @@
|
|||||||
"escape-string-regexp@5.0.0": {
|
"escape-string-regexp@5.0.0": {
|
||||||
"integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw=="
|
"integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw=="
|
||||||
},
|
},
|
||||||
|
"events@3.3.0": {
|
||||||
|
"integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q=="
|
||||||
|
},
|
||||||
"extend@3.0.2": {
|
"extend@3.0.2": {
|
||||||
"integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="
|
"integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="
|
||||||
},
|
},
|
||||||
@@ -485,15 +513,47 @@
|
|||||||
"github-slugger@2.0.0": {
|
"github-slugger@2.0.0": {
|
||||||
"integrity": "sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw=="
|
"integrity": "sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw=="
|
||||||
},
|
},
|
||||||
|
"is-network-error@1.3.2": {
|
||||||
|
"integrity": "sha512-PhBY86zaxNZUuWP6h13Vu5oFe0XY6/UlKzQnYFELzGVHygP3MxmvTfYSG7GN3aIab/iWudSMgjSnG9Dq+nHrgA=="
|
||||||
|
},
|
||||||
"is-plain-obj@4.1.0": {
|
"is-plain-obj@4.1.0": {
|
||||||
"integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg=="
|
"integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg=="
|
||||||
},
|
},
|
||||||
|
"loglevel@1.9.2": {
|
||||||
|
"integrity": "sha512-HgMmCqIJSAKqo68l0rS2AanEWfkxaZ5wNiEFb5ggm08lDs9Xl2KxBlX3PTcaD2chBM1gXAYf491/M2Rv8Jwayg=="
|
||||||
|
},
|
||||||
"longest-streak@3.1.0": {
|
"longest-streak@3.1.0": {
|
||||||
"integrity": "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g=="
|
"integrity": "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g=="
|
||||||
},
|
},
|
||||||
"markdown-table@3.0.4": {
|
"markdown-table@3.0.4": {
|
||||||
"integrity": "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw=="
|
"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": {
|
"mdast-util-find-and-replace@3.0.2": {
|
||||||
"integrity": "sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==",
|
"integrity": "sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==",
|
||||||
"dependencies": [
|
"dependencies": [
|
||||||
@@ -886,6 +946,12 @@
|
|||||||
"moment"
|
"moment"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"p-retry@8.0.0": {
|
||||||
|
"integrity": "sha512-kFVqH1HxOHp8LupNsOys7bSV09VYTRLxarH/mokO4Rqhk6wGi70E0jh4VzvVGXfEVNggHoHLAMWsQqHyU1Ey9A==",
|
||||||
|
"dependencies": [
|
||||||
|
"is-network-error"
|
||||||
|
]
|
||||||
|
},
|
||||||
"remark-frontmatter@5.0.0": {
|
"remark-frontmatter@5.0.0": {
|
||||||
"integrity": "sha512-XTFYvNASMe5iPN0719nPrdItC9aU0ssC4v14mH1BCi1u0n1gAocqcujWUrByftZTbLhRtiKRyjYTSIOcr69UVQ==",
|
"integrity": "sha512-XTFYvNASMe5iPN0719nPrdItC9aU0ssC4v14mH1BCi1u0n1gAocqcujWUrByftZTbLhRtiKRyjYTSIOcr69UVQ==",
|
||||||
"dependencies": [
|
"dependencies": [
|
||||||
@@ -923,6 +989,10 @@
|
|||||||
"unified"
|
"unified"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"sdp-transform@3.0.0": {
|
||||||
|
"integrity": "sha512-gfYVRGxjHkGF2NPeUWHw5u6T/KGFtS5/drPms73gaSuMaVHKCY3lpLnGDfswVQO0kddeePoti09AwhYP4zA8dQ==",
|
||||||
|
"bin": true
|
||||||
|
},
|
||||||
"style-mod@4.1.3": {
|
"style-mod@4.1.3": {
|
||||||
"integrity": "sha512-i/n8VsZydrugj3Iuzll8+x/00GH2vnYsk1eomD8QiRrSAeW6ItbCQDtfXCeJHd0iwiNagqjQkvpvREEPtW3IoQ=="
|
"integrity": "sha512-i/n8VsZydrugj3Iuzll8+x/00GH2vnYsk1eomD8QiRrSAeW6ItbCQDtfXCeJHd0iwiNagqjQkvpvREEPtW3IoQ=="
|
||||||
},
|
},
|
||||||
@@ -933,6 +1003,9 @@
|
|||||||
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
||||||
"bin": true
|
"bin": true
|
||||||
},
|
},
|
||||||
|
"unhomoglyph@1.0.6": {
|
||||||
|
"integrity": "sha512-7uvcWI3hWshSADBu4JpnyYbTVc7YlhF5GDW/oPD5AxIxl34k4wXR3WDkPnzLxkN32LiTCTKMQLtKVZiwki3zGg=="
|
||||||
|
},
|
||||||
"unified@11.0.5": {
|
"unified@11.0.5": {
|
||||||
"integrity": "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==",
|
"integrity": "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==",
|
||||||
"dependencies": [
|
"dependencies": [
|
||||||
@@ -1019,6 +1092,7 @@
|
|||||||
"npm:@flowershow/remark-wiki-link@^3.4.0",
|
"npm:@flowershow/remark-wiki-link@^3.4.0",
|
||||||
"npm:builtin-modules@5",
|
"npm:builtin-modules@5",
|
||||||
"npm:esbuild@~0.27.3",
|
"npm:esbuild@~0.27.3",
|
||||||
|
"npm:matrix-js-sdk@42",
|
||||||
"npm:mdast@3",
|
"npm:mdast@3",
|
||||||
"npm:obsidian@^1.12.2",
|
"npm:obsidian@^1.12.2",
|
||||||
"npm:remark-frontmatter@5",
|
"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": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1774709303,
|
"lastModified": 1782467914,
|
||||||
"narHash": "sha256-D3Q07BbIA2KnTcSXIqqu9P586uWxN74zNoCH3h2ESHg=",
|
"narHash": "sha256-pGvFkM8N0xEkIIXDe5YYfbEAvHrk4IxBrjB/x8OomhE=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "8110df5ad7abf5d4c0f6fb0f8f978390e77f9685",
|
"rev": "e73de5be04e0eff4190a1432b946d469c794e7b4",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
|||||||
27
flake.nix
27
flake.nix
@@ -1,6 +1,5 @@
|
|||||||
{
|
{
|
||||||
inputs = {
|
inputs = {
|
||||||
# nixpkgs 35.11 still contains rust 1.73
|
|
||||||
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
||||||
|
|
||||||
fenix = {
|
fenix = {
|
||||||
@@ -30,7 +29,6 @@
|
|||||||
let
|
let
|
||||||
pkgs = nixpkgs.legacyPackages.${system};
|
pkgs = nixpkgs.legacyPackages.${system};
|
||||||
|
|
||||||
#os
|
|
||||||
osToolchain = fenix.packages.${system}.latest.toolchain;
|
osToolchain = fenix.packages.${system}.latest.toolchain;
|
||||||
osCrane = (crane.mkLib pkgs).overrideToolchain osToolchain;
|
osCrane = (crane.mkLib pkgs).overrideToolchain osToolchain;
|
||||||
|
|
||||||
@@ -177,6 +175,10 @@ in {
|
|||||||
pkgs.pkg-config
|
pkgs.pkg-config
|
||||||
pkgs.gobject-introspection
|
pkgs.gobject-introspection
|
||||||
pkgs.gobject-introspection.dev
|
pkgs.gobject-introspection.dev
|
||||||
|
pkgs.systemd
|
||||||
|
pkgs.rsync
|
||||||
|
pkgs.deno
|
||||||
|
pkgs.openssh
|
||||||
|
|
||||||
# thanks to: https://github.com/NixOS/nixpkgs/pull/496279/changes
|
# thanks to: https://github.com/NixOS/nixpkgs/pull/496279/changes
|
||||||
(pkgs.buildWasmBindgenCli rec {
|
(pkgs.buildWasmBindgenCli rec {
|
||||||
@@ -233,7 +235,7 @@ in {
|
|||||||
];
|
];
|
||||||
|
|
||||||
};
|
};
|
||||||
default = pkgs.mkShell {
|
default = pkgs.mkShell rec {
|
||||||
|
|
||||||
MIZE_BUILD_CONFIG = let
|
MIZE_BUILD_CONFIG = let
|
||||||
settingsFormat = pkgs.formats.toml { };
|
settingsFormat = pkgs.formats.toml { };
|
||||||
@@ -252,6 +254,15 @@ in {
|
|||||||
pkgs.pkg-config
|
pkgs.pkg-config
|
||||||
pkgs.gobject-introspection
|
pkgs.gobject-introspection
|
||||||
pkgs.gobject-introspection.dev
|
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
|
# thanks to: https://github.com/NixOS/nixpkgs/pull/496279/changes
|
||||||
(pkgs.buildWasmBindgenCli rec {
|
(pkgs.buildWasmBindgenCli rec {
|
||||||
@@ -305,8 +316,18 @@ in {
|
|||||||
harfbuzz
|
harfbuzz
|
||||||
librsvg
|
librsvg
|
||||||
libsoup_3
|
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]
|
[dependencies]
|
||||||
quote = "1.0.44"
|
quote = "1.0.44"
|
||||||
syn = "2.0.114"
|
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",
|
"name": "@ppc/marts",
|
||||||
"imports": {},
|
"imports": {},
|
||||||
"exports": {
|
"exports": {
|
||||||
".": "./index.ts"
|
".": "./src/index.ts"
|
||||||
},
|
},
|
||||||
"tasks": {}
|
"tasks": {}
|
||||||
}
|
}
|
||||||
@@ -8,4 +8,7 @@ name = "generated"
|
|||||||
path = "src/lib.rs"
|
path = "src/lib.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
spacetimedb-sdk = "2.1.0"
|
spacetimedb-sdk = "2.6.0"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
target-js = [ "spacetimedb-sdk/browser" ]
|
||||||
@@ -1,2 +1 @@
|
|||||||
pub mod spacetime;
|
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 start: __sdk::Timestamp,
|
||||||
pub end: __sdk::Timestamp,
|
pub end: __sdk::Timestamp,
|
||||||
pub action: Action,
|
pub action: Action,
|
||||||
|
pub user: __sdk::Identity,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl __sdk::InModule for LogEvent {
|
impl __sdk::InModule for LogEvent {
|
||||||
@@ -27,6 +28,7 @@ pub struct LogEventCols {
|
|||||||
pub start: __sdk::__query_builder::Col<LogEvent, __sdk::Timestamp>,
|
pub start: __sdk::__query_builder::Col<LogEvent, __sdk::Timestamp>,
|
||||||
pub end: __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 action: __sdk::__query_builder::Col<LogEvent, Action>,
|
||||||
|
pub user: __sdk::__query_builder::Col<LogEvent, __sdk::Identity>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl __sdk::__query_builder::HasCols for LogEvent {
|
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"),
|
start: __sdk::__query_builder::Col::new(table_name, "start"),
|
||||||
end: __sdk::__query_builder::Col::new(table_name, "end"),
|
end: __sdk::__query_builder::Col::new(table_name, "end"),
|
||||||
action: __sdk::__query_builder::Col::new(table_name, "action"),
|
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
|
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
|
||||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
// 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)]
|
#![allow(unused, clippy::all)]
|
||||||
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
||||||
@@ -10,8 +10,16 @@ pub mod action_type;
|
|||||||
pub mod activities_table;
|
pub mod activities_table;
|
||||||
pub mod activity_type;
|
pub mod activity_type;
|
||||||
pub mod add_activity_reducer;
|
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_event_type;
|
||||||
pub mod log_events_table;
|
pub mod log_events_table;
|
||||||
|
pub mod migrate_procedure;
|
||||||
pub mod next_action_reducer;
|
pub mod next_action_reducer;
|
||||||
pub mod profile_type;
|
pub mod profile_type;
|
||||||
pub mod profiles_table;
|
pub mod profiles_table;
|
||||||
@@ -21,6 +29,9 @@ pub mod task_part_table;
|
|||||||
pub mod task_part_type;
|
pub mod task_part_type;
|
||||||
pub mod task_type;
|
pub mod task_type;
|
||||||
pub mod tasks_table;
|
pub mod tasks_table;
|
||||||
|
pub mod test_procedure;
|
||||||
|
pub mod thought_type;
|
||||||
|
pub mod thoughts_table;
|
||||||
pub mod update_activity_reducer;
|
pub mod update_activity_reducer;
|
||||||
pub mod update_upcoming_reducer;
|
pub mod update_upcoming_reducer;
|
||||||
|
|
||||||
@@ -28,8 +39,16 @@ pub use action_type::Action;
|
|||||||
pub use activities_table::*;
|
pub use activities_table::*;
|
||||||
pub use activity_type::Activity;
|
pub use activity_type::Activity;
|
||||||
pub use add_activity_reducer::add_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_event_type::LogEvent;
|
||||||
pub use log_events_table::*;
|
pub use log_events_table::*;
|
||||||
|
pub use migrate_procedure::migrate;
|
||||||
pub use next_action_reducer::next_action;
|
pub use next_action_reducer::next_action;
|
||||||
pub use profile_type::Profile;
|
pub use profile_type::Profile;
|
||||||
pub use profiles_table::*;
|
pub use profiles_table::*;
|
||||||
@@ -39,6 +58,9 @@ pub use task_part_table::*;
|
|||||||
pub use task_part_type::TaskPart;
|
pub use task_part_type::TaskPart;
|
||||||
pub use task_type::Task;
|
pub use task_type::Task;
|
||||||
pub use tasks_table::*;
|
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_activity_reducer::update_activity;
|
||||||
pub use update_upcoming_reducer::update_upcoming;
|
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.
|
/// to indicate which reducer caused the event.
|
||||||
|
|
||||||
pub enum Reducer {
|
pub enum Reducer {
|
||||||
AddActivity { name: String },
|
AddActivity {
|
||||||
NextAction,
|
name: String,
|
||||||
RemoveActivity { activity: Activity },
|
},
|
||||||
SetActivityName { id: u64, name: String },
|
AddThought {
|
||||||
UpdateActivity { activity: Activity },
|
thought: String,
|
||||||
UpdateUpcoming { upcoming: Vec<Action> },
|
},
|
||||||
|
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 {
|
impl __sdk::InModule for Reducer {
|
||||||
@@ -66,7 +105,8 @@ impl __sdk::Reducer for Reducer {
|
|||||||
fn reducer_name(&self) -> &'static str {
|
fn reducer_name(&self) -> &'static str {
|
||||||
match self {
|
match self {
|
||||||
Reducer::AddActivity { .. } => "add_activity",
|
Reducer::AddActivity { .. } => "add_activity",
|
||||||
Reducer::NextAction => "next_action",
|
Reducer::AddThought { .. } => "add_thought",
|
||||||
|
Reducer::NextAction { .. } => "next_action",
|
||||||
Reducer::RemoveActivity { .. } => "remove_activity",
|
Reducer::RemoveActivity { .. } => "remove_activity",
|
||||||
Reducer::SetActivityName { .. } => "set_activity_name",
|
Reducer::SetActivityName { .. } => "set_activity_name",
|
||||||
Reducer::UpdateActivity { .. } => "update_activity",
|
Reducer::UpdateActivity { .. } => "update_activity",
|
||||||
@@ -80,7 +120,17 @@ impl __sdk::Reducer for Reducer {
|
|||||||
Reducer::AddActivity { name } => {
|
Reducer::AddActivity { name } => {
|
||||||
__sats::bsatn::to_vec(&add_activity_reducer::AddActivityArgs { name: name.clone() })
|
__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 } => {
|
Reducer::RemoveActivity { activity } => {
|
||||||
__sats::bsatn::to_vec(&remove_activity_reducer::RemoveActivityArgs {
|
__sats::bsatn::to_vec(&remove_activity_reducer::RemoveActivityArgs {
|
||||||
activity: activity.clone(),
|
activity: activity.clone(),
|
||||||
@@ -112,10 +162,13 @@ impl __sdk::Reducer for Reducer {
|
|||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub struct DbUpdate {
|
pub struct DbUpdate {
|
||||||
activities: __sdk::TableUpdate<Activity>,
|
activities: __sdk::TableUpdate<Activity>,
|
||||||
|
daily_task: __sdk::TableUpdate<DailyTask>,
|
||||||
|
daily_task_log: __sdk::TableUpdate<DailyTaskLog>,
|
||||||
log_events: __sdk::TableUpdate<LogEvent>,
|
log_events: __sdk::TableUpdate<LogEvent>,
|
||||||
profiles: __sdk::TableUpdate<Profile>,
|
profiles: __sdk::TableUpdate<Profile>,
|
||||||
task_part: __sdk::TableUpdate<TaskPart>,
|
task_part: __sdk::TableUpdate<TaskPart>,
|
||||||
tasks: __sdk::TableUpdate<Task>,
|
tasks: __sdk::TableUpdate<Task>,
|
||||||
|
thoughts: __sdk::TableUpdate<Thought>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<__ws::v2::TransactionUpdate> for DbUpdate {
|
impl TryFrom<__ws::v2::TransactionUpdate> for DbUpdate {
|
||||||
@@ -127,6 +180,12 @@ impl TryFrom<__ws::v2::TransactionUpdate> for DbUpdate {
|
|||||||
"activities" => db_update
|
"activities" => db_update
|
||||||
.activities
|
.activities
|
||||||
.append(activities_table::parse_table_update(table_update)?),
|
.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" => db_update
|
||||||
.log_events
|
.log_events
|
||||||
.append(log_events_table::parse_table_update(table_update)?),
|
.append(log_events_table::parse_table_update(table_update)?),
|
||||||
@@ -139,6 +198,9 @@ impl TryFrom<__ws::v2::TransactionUpdate> for DbUpdate {
|
|||||||
"tasks" => db_update
|
"tasks" => db_update
|
||||||
.tasks
|
.tasks
|
||||||
.append(tasks_table::parse_table_update(table_update)?),
|
.append(tasks_table::parse_table_update(table_update)?),
|
||||||
|
"thoughts" => db_update
|
||||||
|
.thoughts
|
||||||
|
.append(thoughts_table::parse_table_update(table_update)?),
|
||||||
|
|
||||||
unknown => {
|
unknown => {
|
||||||
return Err(__sdk::InternalError::unknown_name(
|
return Err(__sdk::InternalError::unknown_name(
|
||||||
@@ -168,6 +230,9 @@ impl __sdk::DbUpdate for DbUpdate {
|
|||||||
diff.activities = cache
|
diff.activities = cache
|
||||||
.apply_diff_to_table::<Activity>("activities", &self.activities)
|
.apply_diff_to_table::<Activity>("activities", &self.activities)
|
||||||
.with_updates_by_pk(|row| &row.id);
|
.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
|
diff.log_events = cache
|
||||||
.apply_diff_to_table::<LogEvent>("log_events", &self.log_events)
|
.apply_diff_to_table::<LogEvent>("log_events", &self.log_events)
|
||||||
.with_updates_by_pk(|row| &row.id);
|
.with_updates_by_pk(|row| &row.id);
|
||||||
@@ -180,6 +245,9 @@ impl __sdk::DbUpdate for DbUpdate {
|
|||||||
diff.tasks = cache
|
diff.tasks = cache
|
||||||
.apply_diff_to_table::<Task>("tasks", &self.tasks)
|
.apply_diff_to_table::<Task>("tasks", &self.tasks)
|
||||||
.with_updates_by_pk(|row| &row.id);
|
.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
|
diff
|
||||||
}
|
}
|
||||||
@@ -190,6 +258,12 @@ impl __sdk::DbUpdate for DbUpdate {
|
|||||||
"activities" => db_update
|
"activities" => db_update
|
||||||
.activities
|
.activities
|
||||||
.append(__sdk::parse_row_list_as_inserts(table_rows.rows)?),
|
.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" => db_update
|
||||||
.log_events
|
.log_events
|
||||||
.append(__sdk::parse_row_list_as_inserts(table_rows.rows)?),
|
.append(__sdk::parse_row_list_as_inserts(table_rows.rows)?),
|
||||||
@@ -202,6 +276,9 @@ impl __sdk::DbUpdate for DbUpdate {
|
|||||||
"tasks" => db_update
|
"tasks" => db_update
|
||||||
.tasks
|
.tasks
|
||||||
.append(__sdk::parse_row_list_as_inserts(table_rows.rows)?),
|
.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 => {
|
unknown => {
|
||||||
return Err(
|
return Err(
|
||||||
__sdk::InternalError::unknown_name("table", unknown, "QueryRows").into(),
|
__sdk::InternalError::unknown_name("table", unknown, "QueryRows").into(),
|
||||||
@@ -218,6 +295,12 @@ impl __sdk::DbUpdate for DbUpdate {
|
|||||||
"activities" => db_update
|
"activities" => db_update
|
||||||
.activities
|
.activities
|
||||||
.append(__sdk::parse_row_list_as_deletes(table_rows.rows)?),
|
.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" => db_update
|
||||||
.log_events
|
.log_events
|
||||||
.append(__sdk::parse_row_list_as_deletes(table_rows.rows)?),
|
.append(__sdk::parse_row_list_as_deletes(table_rows.rows)?),
|
||||||
@@ -230,6 +313,9 @@ impl __sdk::DbUpdate for DbUpdate {
|
|||||||
"tasks" => db_update
|
"tasks" => db_update
|
||||||
.tasks
|
.tasks
|
||||||
.append(__sdk::parse_row_list_as_deletes(table_rows.rows)?),
|
.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 => {
|
unknown => {
|
||||||
return Err(
|
return Err(
|
||||||
__sdk::InternalError::unknown_name("table", unknown, "QueryRows").into(),
|
__sdk::InternalError::unknown_name("table", unknown, "QueryRows").into(),
|
||||||
@@ -246,10 +332,13 @@ impl __sdk::DbUpdate for DbUpdate {
|
|||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub struct AppliedDiff<'r> {
|
pub struct AppliedDiff<'r> {
|
||||||
activities: __sdk::TableAppliedDiff<'r, Activity>,
|
activities: __sdk::TableAppliedDiff<'r, Activity>,
|
||||||
|
daily_task: __sdk::TableAppliedDiff<'r, DailyTask>,
|
||||||
|
daily_task_log: __sdk::TableAppliedDiff<'r, DailyTaskLog>,
|
||||||
log_events: __sdk::TableAppliedDiff<'r, LogEvent>,
|
log_events: __sdk::TableAppliedDiff<'r, LogEvent>,
|
||||||
profiles: __sdk::TableAppliedDiff<'r, Profile>,
|
profiles: __sdk::TableAppliedDiff<'r, Profile>,
|
||||||
task_part: __sdk::TableAppliedDiff<'r, TaskPart>,
|
task_part: __sdk::TableAppliedDiff<'r, TaskPart>,
|
||||||
tasks: __sdk::TableAppliedDiff<'r, Task>,
|
tasks: __sdk::TableAppliedDiff<'r, Task>,
|
||||||
|
thoughts: __sdk::TableAppliedDiff<'r, Thought>,
|
||||||
__unused: std::marker::PhantomData<&'r ()>,
|
__unused: std::marker::PhantomData<&'r ()>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -264,10 +353,17 @@ impl<'r> __sdk::AppliedDiff<'r> for AppliedDiff<'r> {
|
|||||||
callbacks: &mut __sdk::DbCallbacks<RemoteModule>,
|
callbacks: &mut __sdk::DbCallbacks<RemoteModule>,
|
||||||
) {
|
) {
|
||||||
callbacks.invoke_table_row_callbacks::<Activity>("activities", &self.activities, event);
|
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::<LogEvent>("log_events", &self.log_events, event);
|
||||||
callbacks.invoke_table_row_callbacks::<Profile>("profiles", &self.profiles, 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::<TaskPart>("task_part", &self.task_part, event);
|
||||||
callbacks.invoke_table_row_callbacks::<Task>("tasks", &self.tasks, 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>) {
|
fn register_tables(client_cache: &mut __sdk::ClientCache<Self>) {
|
||||||
activities_table::register_table(client_cache);
|
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);
|
log_events_table::register_table(client_cache);
|
||||||
profiles_table::register_table(client_cache);
|
profiles_table::register_table(client_cache);
|
||||||
task_part_table::register_table(client_cache);
|
task_part_table::register_table(client_cache);
|
||||||
tasks_table::register_table(client_cache);
|
tasks_table::register_table(client_cache);
|
||||||
|
thoughts_table::register_table(client_cache);
|
||||||
}
|
}
|
||||||
const ALL_TABLE_NAMES: &'static [&'static str] =
|
const ALL_TABLE_NAMES: &'static [&'static str] = &[
|
||||||
&["activities", "log_events", "profiles", "task_part", "tasks"];
|
"activities",
|
||||||
|
"daily_task",
|
||||||
|
"daily_task_log",
|
||||||
|
"log_events",
|
||||||
|
"profiles",
|
||||||
|
"task_part",
|
||||||
|
"tasks",
|
||||||
|
"thoughts",
|
||||||
|
];
|
||||||
}
|
}
|
||||||
@@ -4,13 +4,21 @@
|
|||||||
#![allow(unused, clippy::all)]
|
#![allow(unused, clippy::all)]
|
||||||
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
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)]
|
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
|
||||||
#[sats(crate = __lib)]
|
#[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 {
|
impl From<NextActionArgs> for super::Reducer {
|
||||||
fn from(args: NextActionArgs) -> Self {
|
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,
|
/// The reducer will run asynchronously in the future,
|
||||||
/// and this method provides no way to listen for its completion status.
|
/// 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.
|
/// /// Use [`next_action:next_action_then`] to run a callback after the reducer completes.
|
||||||
fn next_action(&self) -> __sdk::Result<()> {
|
fn next_action(&self, break_flag: bool, action: Option<Action>) -> __sdk::Result<()> {
|
||||||
self.next_action_then(|_, _| {})
|
self.next_action_then(break_flag, action, |_, _| {})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Request that the remote module invoke the reducer `next_action` to run as soon as possible,
|
/// 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`.
|
/// and its status can be observed with the `callback`.
|
||||||
fn next_action_then(
|
fn next_action_then(
|
||||||
&self,
|
&self,
|
||||||
|
break_flag: bool,
|
||||||
|
action: Option<Action>,
|
||||||
|
|
||||||
callback: impl FnOnce(&super::ReducerEventContext, Result<Result<(), String>, __sdk::InternalError>)
|
callback: impl FnOnce(&super::ReducerEventContext, Result<Result<(), String>, __sdk::InternalError>)
|
||||||
+ Send
|
+ Send
|
||||||
@@ -51,12 +61,14 @@ pub trait next_action {
|
|||||||
impl next_action for super::RemoteReducers {
|
impl next_action for super::RemoteReducers {
|
||||||
fn next_action_then(
|
fn next_action_then(
|
||||||
&self,
|
&self,
|
||||||
|
break_flag: bool,
|
||||||
|
action: Option<Action>,
|
||||||
|
|
||||||
callback: impl FnOnce(&super::ReducerEventContext, Result<Result<(), String>, __sdk::InternalError>)
|
callback: impl FnOnce(&super::ReducerEventContext, Result<Result<(), String>, __sdk::InternalError>)
|
||||||
+ Send
|
+ Send
|
||||||
+ 'static,
|
+ 'static,
|
||||||
) -> __sdk::Result<()> {
|
) -> __sdk::Result<()> {
|
||||||
self.imp
|
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"]
|
crate-type = ["cdylib"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
spacetimedb = "2.1.0"
|
spacetimedb = { version = "2.4.0", features = ["unstable"] }
|
||||||
log = "0.4"
|
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 dioxus::prelude::*;
|
||||||
use mize::{mize_err, mize_part, Mize, MizeError, MizePart, MizePartCreate, MizeResult};
|
use mize::{mize_err, mize_part, Mize, MizeError, MizePart, MizePartCreate, MizeResult};
|
||||||
|
use tracing::instrument::WithSubscriber;
|
||||||
|
|
||||||
use crate::cli::CliPart;
|
use crate::cli::CliPart;
|
||||||
use crate::habitica::Habitica;
|
use crate::habitica::Habitica;
|
||||||
use crate::SpacetimePart;
|
use crate::SpacetimePart;
|
||||||
use generated::spacetime::{
|
use generated::spacetime::{
|
||||||
activitiesQueryTableAccess, next_action, remove_activity, task_partQueryTableAccess,
|
Action, ActivitiesTableAccess, Activity, DbConnection, LogEventsTableAccess, TaskPart, TaskPartTableAccess, activitiesQueryTableAccess, next_action, remove_activity, task_partQueryTableAccess, update_activity, update_upcoming
|
||||||
update_activity, update_upcoming, Action, ActivitiesTableAccess, Activity, DbConnection,
|
|
||||||
TaskPart, TaskPartTableAccess,
|
|
||||||
};
|
};
|
||||||
use spacetimedb_sdk::{
|
use spacetimedb_sdk::{
|
||||||
credentials, DbContext, Error, Event, Identity, Status, SubscriptionHandle, Table,
|
credentials, DbContext, Error, Event, Identity, Status, SubscriptionHandle, Table,
|
||||||
TableWithPrimaryKey,
|
TableWithPrimaryKey,
|
||||||
};
|
};
|
||||||
|
|
||||||
mod dashboard;
|
|
||||||
|
|
||||||
#[mize_part]
|
#[mize_part]
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct C2vi {
|
pub struct C2vi {
|
||||||
@@ -63,14 +60,6 @@ pub fn c2vi(mize: &mut Mize) -> MizeResult<()> {
|
|||||||
.as_bool()
|
.as_bool()
|
||||||
.unwrap_or(false)
|
.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);
|
launch_c2vi_dashboard(mize);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -89,8 +78,35 @@ pub fn launch_c2vi_dashboard(mize: &mut Mize) {
|
|||||||
.launch(c2vi_dash_main);
|
.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 {
|
pub fn c2vi_dash_main() -> Element {
|
||||||
let mut current = use_signal_sync(|| "".to_string());
|
let mut current = use_signal_sync(|| "".to_string());
|
||||||
|
//TaskPart::
|
||||||
|
//use_one()
|
||||||
use_effect(move || {
|
use_effect(move || {
|
||||||
let mut mize = use_context::<Mize>();
|
let mut mize = use_context::<Mize>();
|
||||||
let mut spacetime = mize.get_part_native::<SpacetimePart>("spacetime").unwrap();
|
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();
|
let data = ctx.db.task_part().identity().find(&ctx.identity()).unwrap();
|
||||||
current.set(data.current.name.clone());
|
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();
|
.subscribe();
|
||||||
con.db
|
con.db
|
||||||
.task_part()
|
.task_part()
|
||||||
@@ -167,7 +183,7 @@ fn setup_tables(conn: &Connection) -> MizeResult<()> {
|
|||||||
fn register_subcommands(cli: &mut CliPart) {
|
fn register_subcommands(cli: &mut CliPart) {
|
||||||
cli.subcommand(
|
cli.subcommand(
|
||||||
Command::new("c2vi-dash").about("Run the c2vi dashboard gui"),
|
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(
|
cli.subcommand(
|
||||||
Command::new("bed").about("Command to run when c2vi goes to bed"),
|
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() {
|
if !response.status().is_success() {
|
||||||
let status = response.status();
|
let status = response.status();
|
||||||
let text = response.text().unwrap_or_default();
|
let text = response.text().unwrap_or_default();
|
||||||
println!("request_url: {}", url);
|
log::info!("request_url: {}", url);
|
||||||
return Err(mize_err!(
|
return Err(mize_err!(
|
||||||
"Habitica API error: {} {} - {}",
|
"Habitica API error: {} {} - {}",
|
||||||
status.as_u16(),
|
status.as_u16(),
|
||||||
@@ -102,7 +102,7 @@ fn handle_rate_limit(response: &Response) {
|
|||||||
.get("X-RateLimit-Reset")
|
.get("X-RateLimit-Reset")
|
||||||
.and_then(|v| v.to_str().ok());
|
.and_then(|v| v.to_str().ok());
|
||||||
|
|
||||||
println!(
|
log::info!(
|
||||||
"RateLimit: {} | Remaining: {} | Reset: {}",
|
"RateLimit: {} | Remaining: {} | Reset: {}",
|
||||||
limit,
|
limit,
|
||||||
remaining,
|
remaining,
|
||||||
@@ -116,7 +116,7 @@ fn handle_rate_limit(response: &Response) {
|
|||||||
let wait_duration = reset_date.signed_duration_since(now).to_std();
|
let wait_duration = reset_date.signed_duration_since(now).to_std();
|
||||||
if let Ok(wait_duration) = wait_duration {
|
if let Ok(wait_duration) = wait_duration {
|
||||||
let wait_ms = wait_duration.as_millis() as u64 + 1000;
|
let wait_ms = wait_duration.as_millis() as u64 + 1000;
|
||||||
println!(
|
log::info!(
|
||||||
"Waiting {} secs for next rate limit window...",
|
"Waiting {} secs for next rate limit window...",
|
||||||
(wait_ms / 1000)
|
(wait_ms / 1000)
|
||||||
);
|
);
|
||||||
@@ -9,7 +9,7 @@ use crate::JsPart;
|
|||||||
|
|
||||||
#[op2(fast)]
|
#[op2(fast)]
|
||||||
fn op_mize_get_part(#[string] name: &str) {
|
fn op_mize_get_part(#[string] name: &str) {
|
||||||
println!("js wants the part {}", name);
|
log::info!("js wants the part {}", name);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[op2]
|
#[op2]
|
||||||
@@ -22,7 +22,7 @@ fn op_mize_get_config(state: &mut OpState, #[string] key: &str) -> String {
|
|||||||
#[op2(fast)]
|
#[op2(fast)]
|
||||||
fn op_mize_add_part(state: &mut OpState, #[string] key: &str) {
|
fn op_mize_add_part(state: &mut OpState, #[string] key: &str) {
|
||||||
let mize: Mize = state.borrow::<Mize>().clone();
|
let mize: Mize = state.borrow::<Mize>().clone();
|
||||||
println!("adding part {}", key);
|
log::info!("adding part {}", key);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[op2(fast)]
|
#[op2(fast)]
|
||||||
@@ -31,7 +31,7 @@ pub type BoxClosure = Box<dyn for<'a> FnOnce(&'a mut JsRuntime) -> BoxFuture<'a>
|
|||||||
|
|
||||||
impl MizePart for JsPart {
|
impl MizePart for JsPart {
|
||||||
fn run(&mut self, mize: &mut Mize) -> MizeResult<()> {
|
fn run(&mut self, mize: &mut Mize) -> MizeResult<()> {
|
||||||
println!("js part run");
|
log::info!("js part run");
|
||||||
self.sender
|
self.sender
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
@@ -100,27 +100,27 @@ fn js_runtime_thread(
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
println!("js thread waiting for smth");
|
log::info!("js thread waiting for smth");
|
||||||
let msg = receiver.recv().unwrap();
|
let msg = receiver.recv().unwrap();
|
||||||
println!("js thread got smth");
|
log::info!("js thread got smth");
|
||||||
match msg {
|
match msg {
|
||||||
JsRuntimeThreadMessage::RunInitJs(js_code) => {
|
JsRuntimeThreadMessage::RunInitJs(js_code) => {
|
||||||
if let Err(err) = js_runtime.execute_script("[init]", 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) => {
|
JsRuntimeThreadMessage::RunJs(js_code) => {
|
||||||
if let Err(err) = js_runtime.execute_script("[idk]", 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 => {
|
JsRuntimeThreadMessage::DoRunPhase => {
|
||||||
if let Err(err) = js_runtime.execute_script("[runPhase]", "mize.runPhase()") {
|
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(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
|
#![feature(oneshot_channel)]
|
||||||
|
|
||||||
#[cfg(feature = "target-os")]
|
#[cfg(feature = "target-os")]
|
||||||
mod cli;
|
pub mod cli;
|
||||||
#[cfg(feature = "target-os")]
|
#[cfg(feature = "target-os")]
|
||||||
pub use cli::*;
|
pub use cli::*;
|
||||||
|
|
||||||
@@ -9,13 +11,19 @@ pub mod habitica;
|
|||||||
pub use habitica::*;
|
pub use habitica::*;
|
||||||
|
|
||||||
#[cfg(feature = "target-os")]
|
#[cfg(feature = "target-os")]
|
||||||
pub mod c2vi;
|
pub mod c2vi {
|
||||||
|
pub mod part;
|
||||||
|
pub mod dashboard;
|
||||||
|
}
|
||||||
#[cfg(feature = "target-os")]
|
#[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;
|
pub mod spacetime;
|
||||||
#[cfg(feature = "target-os")]
|
|
||||||
pub use spacetime::*;
|
pub use spacetime::*;
|
||||||
|
|
||||||
pub mod auth {
|
pub mod auth {
|
||||||
@@ -24,7 +32,13 @@ pub mod auth {
|
|||||||
}
|
}
|
||||||
pub use auth::*;
|
pub use auth::*;
|
||||||
|
|
||||||
|
pub mod ppc;
|
||||||
|
|
||||||
|
pub mod timetrack;
|
||||||
|
|
||||||
//#[cfg(feature = "target-os")]
|
//#[cfg(feature = "target-os")]
|
||||||
//pub mod js;
|
//pub mod js;
|
||||||
//#[cfg(feature = "target-os")]
|
//#[cfg(feature = "target-os")]
|
||||||
//pub use js::*;
|
//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::*;
|
use dioxus::prelude::*;
|
||||||
|
|
||||||
/// Main PPC website component
|
|
||||||
|
|
||||||
pub fn ppc_website() -> Element {
|
pub fn main_page() -> Element {
|
||||||
rsx! {
|
rsx! {
|
||||||
|
"hello world"
|
||||||
div {
|
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
|
||||||
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;
|
use std::sync::oneshot;
|
||||||
|
|
||||||
|
#[cfg(feature = "target-os")]
|
||||||
use clap::{Arg, ArgAction, ArgMatches, Command};
|
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 mize::{Mize, MizeError, MizePart, MizePartCreate, MizeResult, mize_err, mize_part};
|
||||||
use spacetimedb_sdk::{DbContext, Table};
|
use spacetimedb_sdk::{DbContext, Table};
|
||||||
|
|
||||||
@@ -9,7 +13,8 @@ use generated::spacetime::{
|
|||||||
activitiesQueryTableAccess, next_action, remove_activity, task_partQueryTableAccess,
|
activitiesQueryTableAccess, next_action, remove_activity, task_partQueryTableAccess,
|
||||||
update_activity, update_upcoming,
|
update_activity, update_upcoming,
|
||||||
};
|
};
|
||||||
use marts::spacetime::SpacetimePart;
|
use crate::spacetime::SpacetimePart;
|
||||||
|
use spacetimedb_sdk::TableWithPrimaryKey;
|
||||||
|
|
||||||
#[mize_part]
|
#[mize_part]
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
@@ -18,7 +23,9 @@ pub struct TimeTrack {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn timetrack(mize: &mut Mize) -> MizeResult<()> {
|
pub fn timetrack(mize: &mut Mize) -> MizeResult<()> {
|
||||||
let mut cli = mize.get_part_native::<marts::CliPart>("cli")?;
|
#[cfg(feature = "target-os")] {
|
||||||
|
let mut cli = mize.get_part_native::<crate::CliPart>("cli")?;
|
||||||
|
cli.set_command("ppc");
|
||||||
|
|
||||||
cli.subcommand(
|
cli.subcommand(
|
||||||
Command::new("editActionList").alias("aal").arg(
|
Command::new("editActionList").alias("aal").arg(
|
||||||
@@ -27,29 +34,109 @@ pub fn timetrack(mize: &mut Mize) -> MizeResult<()> {
|
|||||||
.action(ArgAction::SetTrue),
|
.action(ArgAction::SetTrue),
|
||||||
),
|
),
|
||||||
command_edit_action_list,
|
command_edit_action_list,
|
||||||
);
|
)?;
|
||||||
cli.subcommand(Command::new("nextAction").alias("ne"), command_next_action);
|
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(
|
cli.subcommand(
|
||||||
Command::new("printCurrentAction").alias("cu"),
|
Command::new("printCurrentAction").alias("cu"),
|
||||||
command_current_action,
|
command_current_action,
|
||||||
);
|
)?;
|
||||||
cli.subcommand(
|
cli.subcommand(
|
||||||
Command::new("listActions").alias("la"),
|
Command::new("listActions").alias("la"),
|
||||||
command_list_actions,
|
command_list_actions,
|
||||||
);
|
)?;
|
||||||
cli.subcommand(
|
cli.subcommand(
|
||||||
Command::new("listActivities").alias("lac"),
|
Command::new("listActivities").alias("lac"),
|
||||||
command_list_activities,
|
command_list_activities,
|
||||||
);
|
)?;
|
||||||
cli.subcommand(
|
cli.subcommand(
|
||||||
Command::new("editActivities").alias("eac"),
|
Command::new("editActivities").alias("eac"),
|
||||||
command_edit_activities,
|
command_edit_activities,
|
||||||
);
|
)?;
|
||||||
|
mize.add_listener(
|
||||||
|
"timetrack.sub_current",
|
||||||
|
Box::new(timetrack_sub_current_listener),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
mize.register_part(Box::new(TimeTrack { mize: mize.clone() }))?;
|
mize.register_part(Box::new(TimeTrack { mize: mize.clone() }))?;
|
||||||
Ok(())
|
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 MizePartCreate for TimeTrack {}
|
||||||
|
|
||||||
impl MizePart 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 mut spacetime = mize.get_part_native::<SpacetimePart>("spacetime")?;
|
||||||
let con = spacetime.con();
|
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::<()>();
|
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();
|
wait_tx.send(()).unwrap();
|
||||||
})?;
|
})?;
|
||||||
waiter_rx.recv()?;
|
waiter_rx.recv()?;
|
||||||
|
|
||||||
|
let new_action = get_current_action(&mut mize, con)?;
|
||||||
|
out!("new action: {}", new_action.name);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "target-os")]
|
||||||
fn command_current_action(_: &ArgMatches, mut mize: Mize) -> MizeResult<()> {
|
fn command_current_action(_: &ArgMatches, mut mize: Mize) -> MizeResult<()> {
|
||||||
let mut spacetime = mize.get_part_native::<SpacetimePart>("spacetime")?;
|
let mut spacetime = mize.get_part_native::<SpacetimePart>("spacetime")?;
|
||||||
let action = get_current_action(&mut mize, spacetime.con())?;
|
let action = get_current_action(&mut mize, spacetime.con())?;
|
||||||
println!("{}", action.name);
|
out!("{}", action.name);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -106,6 +216,7 @@ fn get_current_action(_mize: &mut Mize, con: &DbConnection) -> MizeResult<Action
|
|||||||
Ok(action)
|
Ok(action)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "target-os")]
|
||||||
fn command_list_activities(_: &ArgMatches, mut mize: Mize) -> MizeResult<()> {
|
fn command_list_activities(_: &ArgMatches, mut mize: Mize) -> MizeResult<()> {
|
||||||
let mut spacetime = mize.get_part_native::<SpacetimePart>("spacetime")?;
|
let mut spacetime = mize.get_part_native::<SpacetimePart>("spacetime")?;
|
||||||
let activities = get_activities(&mut mize, spacetime.con())?;
|
let activities = get_activities(&mut mize, spacetime.con())?;
|
||||||
@@ -124,24 +235,26 @@ fn command_list_activities(_: &ArgMatches, mut mize: Mize) -> MizeResult<()> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "target-os")]
|
||||||
fn command_list_actions(_: &ArgMatches, mut mize: Mize) -> MizeResult<()> {
|
fn command_list_actions(_: &ArgMatches, mut mize: Mize) -> MizeResult<()> {
|
||||||
let mut spacetime = mize.get_part_native::<SpacetimePart>("spacetime")?;
|
let mut spacetime = mize.get_part_native::<SpacetimePart>("spacetime")?;
|
||||||
|
|
||||||
let actions = get_actions(spacetime.con())?;
|
let actions = get_actions(spacetime.con())?;
|
||||||
|
|
||||||
for action in actions {
|
for action in actions {
|
||||||
println!("{}", action.name)
|
out!("{}", action.name)
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "target-os")]
|
||||||
fn command_edit_action_list(matches: &ArgMatches, mut mize: Mize) -> MizeResult<()> {
|
fn command_edit_action_list(matches: &ArgMatches, mut mize: Mize) -> MizeResult<()> {
|
||||||
let mut spacetime = mize.get_part_native::<SpacetimePart>("spacetime")?;
|
let mut spacetime = mize.get_part_native::<SpacetimePart>("spacetime")?;
|
||||||
|
|
||||||
// maybe append a template to the action list
|
// maybe append a template to the action list
|
||||||
if matches.get_flag("defaultTemplate") {
|
if matches.get_flag("defaultTemplate") {
|
||||||
println!("Appending template...");
|
out!("Appending template...");
|
||||||
return Ok(());
|
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))?;
|
.map_err(|e| mize_err!("Failed to write temp file: {}", e))?;
|
||||||
|
|
||||||
// open $EDITOR blocking
|
// open $EDITOR blocking
|
||||||
std::process::Command::new("nvim")
|
crate::cli_spawn(format!("nvim {}", tmp_file.display()))?;
|
||||||
.arg(&tmp_file)
|
|
||||||
.status()
|
|
||||||
.map_err(|e| mize_err!("Failed to run nvim: {}", e))?;
|
|
||||||
|
|
||||||
// read file and update db
|
// read file and update db
|
||||||
let new_file_content = std::fs::read_to_string(&tmp_file)
|
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();
|
let _parts: Vec<&str> = action.name.split_whitespace().collect();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "target-os")]
|
||||||
fn command_edit_activities(_: &ArgMatches, mut mize: Mize) -> MizeResult<()> {
|
fn command_edit_activities(_: &ArgMatches, mut mize: Mize) -> MizeResult<()> {
|
||||||
let mut spacetime = mize.get_part_native::<SpacetimePart>("spacetime")?;
|
let mut spacetime = mize.get_part_native::<SpacetimePart>("spacetime")?;
|
||||||
|
|
||||||
@@ -270,10 +381,7 @@ fn command_edit_activities(_: &ArgMatches, mut mize: Mize) -> MizeResult<()> {
|
|||||||
|
|
||||||
loop {
|
loop {
|
||||||
// open $EDITOR blocking
|
// open $EDITOR blocking
|
||||||
std::process::Command::new("nvim")
|
crate::cli_spawn(format!("nvim {}", tmp_file.display()))?;
|
||||||
.arg(&tmp_file)
|
|
||||||
.status()
|
|
||||||
.map_err(|e| mize_err!("Failed to run nvim: {}", e))?;
|
|
||||||
|
|
||||||
let new_file_content = std::fs::read_to_string(&tmp_file)
|
let new_file_content = std::fs::read_to_string(&tmp_file)
|
||||||
.map_err(|e| mize_err!("Failed to read temp file: {}", e))?;
|
.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) =
|
if let Err(err) =
|
||||||
parse_activities_from_text(&mut mize, con, new_file_content, old_activities)
|
parse_activities_from_text(&mut mize, con, new_file_content, old_activities)
|
||||||
{
|
{
|
||||||
println!("error parsing the activities from the file: {:?}", err);
|
out!("error parsing the activities from the file: {:?}", err);
|
||||||
println!("press ENTER to open the file again");
|
out!("press ENTER to open the file again");
|
||||||
|
|
||||||
// wait for user input
|
// wait for user input
|
||||||
// so that they can se the err msg, before running nvim again
|
// 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(())
|
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>>();
|
let (exit_tx, exit_rx) = std::sync::oneshot::channel::<Vec<Activity>>();
|
||||||
con.subscription_builder()
|
con.subscription_builder()
|
||||||
.on_applied(|ctx| {
|
.on_applied(|ctx| {
|
||||||
@@ -315,7 +423,7 @@ fn get_activities(_mize: &mut Mize, con: &mut DbConnection) -> MizeResult<Vec<Ac
|
|||||||
|
|
||||||
fn parse_activities_from_text(
|
fn parse_activities_from_text(
|
||||||
mize: &mut Mize,
|
mize: &mut Mize,
|
||||||
con: &mut DbConnection,
|
con: &DbConnection,
|
||||||
text: String,
|
text: String,
|
||||||
old_activities: Vec<Activity>,
|
old_activities: Vec<Activity>,
|
||||||
) -> MizeResult<()> {
|
) -> MizeResult<()> {
|
||||||
@@ -385,7 +493,7 @@ fn parse_activities_from_text(
|
|||||||
|
|
||||||
// check for declared parents
|
// check for declared parents
|
||||||
let new_activities = get_activities(mize, con)?;
|
let new_activities = get_activities(mize, con)?;
|
||||||
println!("new_activities: {:?}", new_activities);
|
out!("new_activities: {:?}", new_activities);
|
||||||
for line in text.lines() {
|
for line in text.lines() {
|
||||||
if line == "" {
|
if line == "" {
|
||||||
continue;
|
continue;
|
||||||
@@ -21,9 +21,9 @@ required-features = [ "target-os" ]
|
|||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = [ "target-os" ]
|
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-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"]
|
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/net", "tokio", "tokio/rt-multi-thread", "tokio/io-util"]
|
async = [ "tokio", ]
|
||||||
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
@@ -38,13 +38,10 @@ serde_json = { version = "1.0.117", optional = false }
|
|||||||
toml = { version = "0.8.13", features = ["parse"], optional = false }
|
toml = { version = "0.8.13", features = ["parse"], optional = false }
|
||||||
dyn-clone = "1.0.17"
|
dyn-clone = "1.0.17"
|
||||||
sha2 = "0.10.8"
|
sha2 = "0.10.8"
|
||||||
flume = "0.11.1"
|
flume = { version = "0.11.1", default-features = false, features = [ "async" ] }
|
||||||
mize_macros = { path = "../ac_mize_macros" }
|
mize_macros = { path = "../macros" }
|
||||||
async-trait = "0.1.89"
|
async-trait = "0.1.89"
|
||||||
|
|
||||||
# builtin modules
|
|
||||||
#mize_module_blob = { path = "./modules/modules/Blob" }
|
|
||||||
|
|
||||||
# wasm dependencies
|
# wasm dependencies
|
||||||
wasm-bindgen = { version = "0.2.84", optional = true }
|
wasm-bindgen = { version = "0.2.84", optional = true }
|
||||||
console_error_panic_hook = { version = "0.1.7", 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 }
|
nix = { version = "0.27.1", features = ["signal"], optional = true }
|
||||||
sysinfo = { version = "0.30.12", optional = true }
|
sysinfo = { version = "0.30.12", optional = true }
|
||||||
tracing-subscriber = { version = "0.3.18", 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 }
|
libloading = { version = "0.8.5", optional = true }
|
||||||
ciborium-io = { version = "0.2.2", optional = true }
|
ciborium-io = { version = "0.2.2", optional = true }
|
||||||
tracing-core = { version = "0.1.32", optional = true }
|
tracing-core = { version = "0.1.32", optional = true }
|
||||||
@@ -95,3 +92,8 @@ wasm-bindgen-test = { version = "0.3.34" }
|
|||||||
[profile.release]
|
[profile.release]
|
||||||
# Tell `rustc` to optimize for small code size.
|
# Tell `rustc` to optimize for small code size.
|
||||||
opt-level = "s"
|
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;
|
use colored::Colorize;
|
||||||
|
#[cfg(feature = "target-js")]
|
||||||
|
use wasm_bindgen::JsValue;
|
||||||
use core::result::Result;
|
use core::result::Result;
|
||||||
use core::result::Result::Ok;
|
use core::result::Result::Ok;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
@@ -24,8 +26,8 @@ pub trait MizeResultTrait<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub trait IntoMizeResult<T, S> {
|
pub trait IntoMizeResult<T, S> {
|
||||||
fn mize_result(self) -> MizeResult<T>;
|
fn mres(self) -> MizeResult<T>;
|
||||||
fn mize_result_msg(self, msg: S) -> MizeResult<T>
|
fn mres_msg(self, msg: S) -> MizeResult<T>
|
||||||
where
|
where
|
||||||
S: std::fmt::Display;
|
S: std::fmt::Display;
|
||||||
}
|
}
|
||||||
@@ -125,16 +127,16 @@ impl MizeError {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn log(self) -> MizeError {
|
pub fn log(self) -> MizeError {
|
||||||
error!("MizeError encountered!");
|
println!("[ {} ] MizeError encountered!", "ERROR".red());
|
||||||
|
|
||||||
if let Some(ref location) = self.code_location {
|
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 {
|
for msg in &self.messages {
|
||||||
error!("[ {} ] {}", "MSG".yellow(), msg);
|
println!("[ {} ] {}", "MSG".yellow(), msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
self
|
self
|
||||||
@@ -261,7 +263,7 @@ where
|
|||||||
E: std::fmt::Display,
|
E: std::fmt::Display,
|
||||||
{
|
{
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
fn mize_result(self) -> MizeResult<T> {
|
fn mres(self) -> MizeResult<T> {
|
||||||
let caller_location = std::panic::Location::caller();
|
let caller_location = std::panic::Location::caller();
|
||||||
|
|
||||||
match self {
|
match self {
|
||||||
@@ -274,7 +276,7 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
fn mize_result_msg(self, msg: S) -> MizeResult<T>
|
fn mres_msg(self, msg: S) -> MizeResult<T>
|
||||||
where
|
where
|
||||||
S: std::fmt::Display,
|
S: std::fmt::Display,
|
||||||
{
|
{
|
||||||
@@ -346,3 +348,20 @@ impl From<MizeError> for MizeErrorWhichImplementsError {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl std::error::Error 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::ops::{Deref, DerefMut};
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
|
use std::thread::panicking;
|
||||||
use std::{thread, vec};
|
use std::{thread, vec};
|
||||||
use tracing::{debug, error, info, trace, warn, Instrument};
|
use tracing::{debug, error, info, trace, warn, Instrument};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
@@ -63,7 +64,11 @@ pub struct Mize {
|
|||||||
pub(crate) id_pool: Arc<Mutex<VecStringPool>>,
|
pub(crate) id_pool: Arc<Mutex<VecStringPool>>,
|
||||||
pub(crate) namespace_pool: Arc<Mutex<StringPool>>,
|
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
|
//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
|
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())),
|
parts: Arc::new(Mutex::new(HashMap::new())),
|
||||||
part_names: Arc::new(Mutex::new(Vec::new())),
|
part_names: Arc::new(Mutex::new(Vec::new())),
|
||||||
config_opts: Arc::new(Mutex::new(HashMap::new())),
|
config_opts: Arc::new(Mutex::new(HashMap::new())),
|
||||||
|
listeners: Arc::new(Mutex::new(HashMap::new())),
|
||||||
connections,
|
connections,
|
||||||
subs,
|
subs,
|
||||||
id_pool,
|
id_pool,
|
||||||
@@ -270,8 +276,9 @@ impl Mize {
|
|||||||
create_msg_wait,
|
create_msg_wait,
|
||||||
|
|
||||||
#[cfg(feature = "async")]
|
#[cfg(feature = "async")]
|
||||||
|
#[cfg(not(feature = "target-js"))]
|
||||||
runtime: Arc::new(Mutex::new(
|
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));
|
self.parts.lock().unwrap().insert(part.name(), Some(part));
|
||||||
Ok(())
|
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> {
|
fn part_names(&mut self) -> Vec<&'static str> {
|
||||||
self.part_names.lock().unwrap().clone()
|
self.part_names.lock().unwrap().clone()
|
||||||
}
|
}
|
||||||
@@ -372,9 +391,16 @@ impl Mize {
|
|||||||
// end of platform specific init code
|
// end of platform specific init code
|
||||||
|
|
||||||
// load the modules, as specified in the parts config
|
// 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) => {
|
Ok(parts_to_load) => {
|
||||||
for part_name in parts_to_load.split(" ") {
|
for part_name in parts_to_load.split(" ") {
|
||||||
|
if part_name.trim().is_empty() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
self.load_part(part_name)?;
|
self.load_part(part_name)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -390,14 +416,35 @@ impl Mize {
|
|||||||
pub fn with_config(config: ItemData) -> MizeResult<Mize> {
|
pub fn with_config(config: ItemData) -> MizeResult<Mize> {
|
||||||
trace!("[ {} ] Instance::with_config()", "CALL".yellow());
|
trace!("[ {} ] Instance::with_config()", "CALL".yellow());
|
||||||
trace!("config: {}", config);
|
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<()> {
|
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()?)
|
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();
|
let mut instance_clone = self.clone();
|
||||||
self.spawn_background("some_listener", move || listener.listen(instance_clone));
|
self.spawn_background("some_listener", move || listener.listen(instance_clone));
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -637,6 +684,21 @@ impl Mize {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn report_err(&self, err: MizeError) {
|
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();
|
err.log();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -751,13 +813,16 @@ impl Mize {
|
|||||||
let my_thread_id_no_mutex_guard = *next_thread_id;
|
let my_thread_id_no_mutex_guard = *next_thread_id;
|
||||||
let thread_mutex = self.threads.clone();
|
let thread_mutex = self.threads.clone();
|
||||||
let name_to_move = name.to_owned();
|
let name_to_move = name.to_owned();
|
||||||
let to_spawn = move || -> MizeResult<()> {
|
let to_spawn = move || -> () {
|
||||||
debug!("spawning thread: {}", name_to_move);
|
debug!("spawning thread: {}", name_to_move);
|
||||||
let my_thread_id = my_thread_id_no_mutex_guard;
|
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
|
*threads_inner = threads_inner
|
||||||
.clone()
|
.clone()
|
||||||
@@ -769,7 +834,6 @@ impl Mize {
|
|||||||
.collect();
|
.collect();
|
||||||
*/
|
*/
|
||||||
debug!("thread '{}' stopped", name_to_move);
|
debug!("thread '{}' stopped", name_to_move);
|
||||||
Ok(())
|
|
||||||
};
|
};
|
||||||
|
|
||||||
*next_thread_id += 1;
|
*next_thread_id += 1;
|
||||||
@@ -948,7 +1012,10 @@ impl Mize {
|
|||||||
let mut dyn_guard = self.get_part(name)?;
|
let mut dyn_guard = self.get_part(name)?;
|
||||||
let part = dyn_guard.part.take().unwrap();
|
let part = dyn_guard.part.take().unwrap();
|
||||||
drop(dyn_guard);
|
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 {
|
Ok(MizePartGuard {
|
||||||
mize: self.clone(),
|
mize: self.clone(),
|
||||||
part: Some(*concrete_part),
|
part: Some(*concrete_part),
|
||||||
@@ -215,6 +215,19 @@ fn handle_msg(msg: &mut MizeMessage, instance: &Mize) -> MizeResult<()> {
|
|||||||
}
|
}
|
||||||
return Ok(());
|
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"));
|
return Err(mize_err!("got a message, that is not handeled"));
|
||||||
}
|
}
|
||||||
@@ -50,7 +50,7 @@ impl Item<'_> {
|
|||||||
pub fn value_string(&self) -> MizeResult<String> {
|
pub fn value_string(&self) -> MizeResult<String> {
|
||||||
let raw = self.value_raw()?;
|
let raw = self.value_raw()?;
|
||||||
String::from_utf8(raw.clone())
|
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> {
|
pub fn as_data_full(&self) -> MizeResult<ItemData> {
|
||||||
@@ -193,7 +193,7 @@ impl ItemData {
|
|||||||
pub fn from_toml(toml_string: &str) -> MizeResult<ItemData> {
|
pub fn from_toml(toml_string: &str) -> MizeResult<ItemData> {
|
||||||
let toml_deserializer = toml::Deserializer::new(&toml_string);
|
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: {}",
|
"Could not deserialize the toml string: {}",
|
||||||
&toml_string
|
&toml_string
|
||||||
))?;
|
))?;
|
||||||
@@ -246,7 +246,7 @@ impl ItemData {
|
|||||||
pub fn from_json(json_str: String) -> MizeResult<ItemData> {
|
pub fn from_json(json_str: String) -> MizeResult<ItemData> {
|
||||||
let mut json_deserializer = serde_json::Deserializer::from_str(json_str.as_str());
|
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: {}",
|
"could not deserialize the json string: {}",
|
||||||
json_str
|
json_str
|
||||||
))?;
|
))?;
|
||||||
@@ -682,6 +682,9 @@ pub trait IntoItemData {
|
|||||||
|
|
||||||
impl IntoPath for &str {
|
impl IntoPath for &str {
|
||||||
fn into_path(self) -> Vec<String> {
|
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()
|
self.to_owned().split("/").map(|v| v.to_owned()).collect()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -704,7 +707,7 @@ impl IntoPath for Vec<&str> {
|
|||||||
|
|
||||||
impl IntoItemData for &str {
|
impl IntoItemData for &str {
|
||||||
fn into_item_data(self) -> ItemData {
|
fn into_item_data(self) -> ItemData {
|
||||||
ItemData::parse(self)
|
ItemData(self.into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl IntoItemData for String {
|
impl IntoItemData for String {
|
||||||
@@ -717,6 +720,15 @@ impl IntoItemData for &String {
|
|||||||
ItemData(self.to_owned().into())
|
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 {
|
impl IntoItemData for CborValue {
|
||||||
fn into_item_data(self) -> ItemData {
|
fn into_item_data(self) -> ItemData {
|
||||||
ItemData(self)
|
ItemData(self)
|
||||||
@@ -732,6 +744,11 @@ impl IntoItemData for i128 {
|
|||||||
ItemData(self.into())
|
ItemData(self.into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
impl IntoItemData for bool {
|
||||||
|
fn into_item_data(self) -> ItemData {
|
||||||
|
ItemData(self.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl fmt::Display for ItemData {
|
impl fmt::Display for ItemData {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
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> {
|
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",
|
"Could not parse the store_part of mizeid {} into a u64 for the MemStore",
|
||||||
id_str
|
id_str
|
||||||
))
|
))
|
||||||
@@ -17,6 +17,17 @@ pub struct MizeMessage {
|
|||||||
pub conn_id: u64,
|
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
|
// msg items
|
||||||
static MSG_CMD: u16 = 1;
|
static MSG_CMD: u16 = 1;
|
||||||
static MSG_ID: u16 = 2;
|
static MSG_ID: u16 = 2;
|
||||||
@@ -42,6 +53,7 @@ pub enum MessageCmd {
|
|||||||
UpdateRequest,
|
UpdateRequest,
|
||||||
GetSub,
|
GetSub,
|
||||||
Sub,
|
Sub,
|
||||||
|
Custom(ItemData),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MizeMessage {
|
impl MizeMessage {
|
||||||
@@ -49,6 +61,24 @@ impl MizeMessage {
|
|||||||
MizeMessage { value, conn_id }
|
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 {
|
pub fn new_get(id: MizeId, conn_id: u64) -> MizeMessage {
|
||||||
let id_path = id
|
let id_path = id
|
||||||
.path()
|
.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 val_of_c = {
|
||||||
let mut tmp_val = 0;
|
let mut tmp_val = 0;
|
||||||
for (key, val) in msg_as_map {
|
for (key, val) in msg_as_map {
|
||||||
@@ -204,6 +234,8 @@ impl MizeMessage {
|
|||||||
if key_int == &one {
|
if key_int == &one {
|
||||||
if let CborValue::Integer(val_int) = val {
|
if let CborValue::Integer(val_int) = val {
|
||||||
tmp_val = val_int.to_owned().into();
|
tmp_val = val_int.to_owned().into();
|
||||||
|
} else {
|
||||||
|
return Ok(MessageCmd::Custom(ItemData(val.to_owned())));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -214,7 +246,7 @@ impl MizeMessage {
|
|||||||
tmp_val
|
tmp_val
|
||||||
};
|
};
|
||||||
|
|
||||||
// match on value of c
|
// match on value of cmd
|
||||||
let cmd = match val_of_c {
|
let cmd = match val_of_c {
|
||||||
1 => MessageCmd::Get,
|
1 => MessageCmd::Get,
|
||||||
2 => MessageCmd::Update,
|
2 => MessageCmd::Update,
|
||||||
@@ -380,7 +412,7 @@ impl fmt::Display for MizeMessage {
|
|||||||
self.value
|
self.value
|
||||||
.serialize(&mut serde_json::Serializer::pretty(display_writer))
|
.serialize(&mut serde_json::Serializer::pretty(display_writer))
|
||||||
.map_err(|serde_err| std::fmt::Error)
|
.map_err(|serde_err| std::fmt::Error)
|
||||||
.mize_result_msg("serialize error")
|
.mres_msg("serialize error")
|
||||||
.inspect_err(|e| {
|
.inspect_err(|e| {
|
||||||
e.clone().log();
|
e.clone().log();
|
||||||
});
|
});
|
||||||
@@ -2,6 +2,54 @@
|
|||||||
|
|
||||||
static PROTO_VERSION: u8 = 1;
|
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_export]
|
||||||
macro_rules! test_println {
|
macro_rules! test_println {
|
||||||
($($arg:tt)*) => {
|
($($arg:tt)*) => {
|
||||||
@@ -43,6 +91,7 @@ mod core {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub use async_trait::async_trait;
|
pub use async_trait::async_trait;
|
||||||
|
pub use ciborium;
|
||||||
pub use core::error::MizeError;
|
pub use core::error::MizeError;
|
||||||
pub use core::error::MizeResult;
|
pub use core::error::MizeResult;
|
||||||
pub use core::instance::Mize;
|
pub use core::instance::Mize;
|
||||||
@@ -78,7 +127,8 @@ pub mod platform {
|
|||||||
pub use super::os::load_part;
|
pub use super::os::load_part;
|
||||||
|
|
||||||
#[cfg(feature = "target-js")]
|
#[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")))]
|
#[cfg(not(any(feature = "target-os", feature = "target-js")))]
|
||||||
pub use super::super::load_part;
|
pub use super::super::load_part;
|
||||||
@@ -11,10 +11,9 @@ use web_sys::{WorkerOptions, WorkerType};
|
|||||||
|
|
||||||
use crate::core::item::IntoItemData;
|
use crate::core::item::IntoItemData;
|
||||||
use crate::error::MizeResult;
|
use crate::error::MizeResult;
|
||||||
use crate::instance::module::EmptyModule;
|
|
||||||
use crate::Mize;
|
use crate::Mize;
|
||||||
use crate::MizeError;
|
use crate::MizeError;
|
||||||
use crate::{mize_err, Module};
|
use crate::mize_err;
|
||||||
|
|
||||||
// console_log macro
|
// console_log macro
|
||||||
// that can be copied into other files for debugging purposes
|
// 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<()> {
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
async fn load_module_async(mut mize: Mize, name: String, path: Option<String>) -> () {
|
async fn load_module_async(mut mize: Mize, name: String, path: Option<String>) -> () {
|
||||||
let name = name.as_str();
|
let name = name.as_str();
|
||||||
let module_url = fetch_module(&mut mize, name).unwrap();
|
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(".", "/");
|
let module_name_with_slashes = name.replace(".", "/");
|
||||||
if let Ok(module_path) = mize
|
if let Ok(module_path) = mize
|
||||||
.get(format!(
|
.get(format!(
|
||||||
@@ -191,6 +192,7 @@ async fn download_module(mize: &mut Mize, name: &str) -> MizeResult<js_sys::Arra
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[wasm_bindgen]
|
#[wasm_bindgen]
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct JsMize {
|
pub struct JsMize {
|
||||||
inner: NonNull<Mize>,
|
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 config = ItemData::from_json(config_json_str).expect("parsing of json config failed");
|
||||||
|
|
||||||
let mut mize = Mize::empty().expect("Mize::empty() failed");
|
let mut mize = Mize::with_config(config).expect("Mize::new() failed");
|
||||||
|
|
||||||
mize.set_blocking("0/config", config)
|
|
||||||
.expect("Failed to set the config at item 0");
|
|
||||||
|
|
||||||
let mut js_instance = JsMize {
|
let mut js_instance = JsMize {
|
||||||
inner: NonNull::from(Box::leak(Box::new(mize))),
|
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