Compare commits

...

41 Commits

Author SHA1 Message Date
Sebastian Moser
0abf4108e8 two README improvements 2026-11-04 20:25:33 +01:00
Sebastian Moser
9707286375 updated README and a bunch of other stuff 2026-11-04 20:24:14 +01:00
Sebastian Moser
6eedb1826a ppc login works in mme main 2026-11-01 14:49:38 +01:00
Sebastian Moser
66b8713e3a dyn calling works + vibed dioxus ui components + some mo provision prototyping 2026-10-31 17:50:46 +01:00
Sebastian Moser
37cb94d1d4 ... 2026-10-23 15:27:31 +02:00
Sebastian Moser
9141fc5441 fully compiles 2026-10-23 12:22:23 +02:00
Sebastian Moser
2b2d40148b basically fully restored 2026-10-23 12:11:32 +02:00
Sebastian Moser
9078b108a6 more restored 2026-10-23 11:43:55 +02:00
Sebastian Moser
6bd98e7c80 more restored 2026-10-23 11:40:23 +02:00
Sebastian Moser
9e5ceaf0f9 some more restorations 2026-10-23 11:29:39 +02:00
Sebastian Moser
48f7ee2e35 restorations from agent session 2026-10-22 22:45:07 +02:00
Sebastian Moser
387c31259d removed the old src 2026-10-22 22:13:47 +02:00
Sebastian Moser
7f86f3dc5e deleted the old marts 2026-10-22 22:13:26 +02:00
Sebastian Moser
679f5e51e5 changes saved in my editor 2026-10-22 18:25:57 +02:00
Sebastian Moser
105eca862d changed nixos Module 2026-10-14 20:13:04 +02:00
Sebastian Moser
0b316ad54f updated AGENTS.md and pps site works again. 2026-10-14 01:50:08 +02:00
Sebastian Moser
a7db7da9d2 rename sc2 to sci and move old dev-part stuff into the c2vi part 2026-10-12 14:53:10 +02:00
Sebastian Moser
759e6cad66 timetrack works locally 2026-10-11 22:20:46 +02:00
Sebastian Moser
c19de1ed84 added ppc server and mize run external subcommands 2026-09-30 09:37:21 +02:00
Sebastian Moser
e96e93a5a0 the mize bin works 2026-09-19 23:47:12 +00:00
Sebastian Moser
d559159397 recent mize changes 2026-09-16 21:32:53 +00:00
Sebastian Moser
05d410949c got obsidian plugin to build again 2026-07-31 01:39:46 +02:00
Sebastian Moser
b2ed9efff2 omega restructuring + a bunch of changes i have made over the last weeks 2026-07-31 00:48:15 +02:00
Sebastian Moser
c57849f748 added enableConfigFile option to mize daemon nix module 2026-06-08 14:30:13 +02:00
Sebastian Moser
19573502d2 fixed some stuff 2026-05-18 18:22:32 +02:00
Sebastian Moser
d1ddb405c8 moved generated,spacetime into it's own package + many other things 2026-05-17 14:23:34 +02:00
Sebastian Moser
2907d7e236 nixos module needed some fixes 2026-05-14 08:17:01 +02:00
Sebastian Moser
55c85ad716 mize daemon service and some timetrack stuff as well 2026-05-14 07:43:47 +02:00
Sebastian Moser
193c3ce3e0 cleaned up nix code dramatically 2026-05-10 01:47:30 +02:00
Sebastian Moser
5599a48c27 aparently we need to remove the file first for scp to work 2026-05-10 01:47:08 +02:00
Sebastian Moser
c9ea571744 c2vi dash working 2026-05-06 12:48:20 +02:00
Sebastian Moser
562b1abe9c listActions and editActions works 2026-04-02 17:18:23 +02:00
Sebastian Moser
94f0a960f6 idk 2026-03-25 12:13:00 +01:00
Sebastian Moser
299f00c94f dioxus hello world component in obsidian + some other stuff 2026-03-14 17:58:53 +01:00
Sebastian Moser
f1f53e3857 cli login works and hello-world spacetime module 2026-03-14 15:33:27 +01:00
Sebastian Moser
d9e0e81e61 login implemented 2026-03-09 20:20:05 +01:00
Sebastian Moser
9ad752c16f Merge pull request #2 from knz/knz-changes
Knz changes
2026-03-09 20:08:24 +01:00
Raphael 'kena' Poss
d7a0780095 port habitica integration from JS to Rust
The old JS ppc repo had a working habitica integration. This ports all
that functionality into the new Rust-based marts package so it can run
natively without the Deno/JS runtime dependency.

Changes:
- habitica.rs: make api_request public, add get_tasks() and delete_task()
  convenience methods, register config opts
- c2vi/mod.rs: full rewrite implementing all 9 CLI commands (bed,
  clearTodos, listTodos, buyHealthPotion, addActionItems, skip, dumpLog,
  listDailies, printTask) with SQLite persistence via rusqlite
- lib.rs: wire c2vi module into marts exports
- ppc main.rs: register c2vi part at startup
- Cargo.toml: add rusqlite dependency with bundled feature

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 14:22:10 +01:00
Raphael 'kena' Poss
0dc09e7618 simplify AGENTS.md 2026-03-09 14:21:10 +01:00
Sebastian Moser
8e7f2d1ddc idk. but added AGENTS.md 2026-03-06 19:26:07 +01:00
Sebastian Moser
b4bfedf372 added external parser logic 2026-02-24 14:15:17 +01:00
237 changed files with 21599 additions and 33998 deletions

4
.gitignore vendored
View File

@@ -1,12 +1,16 @@
target target
.direnv
# for nix # for nix
result result
dist dist
rust_dist rust_dist
deno_dist
node_modules node_modules
.env .env
mize_config.toml
# I put testing stuff for this projekt that I don't want to push to github into a folder called gitignore # I put testing stuff for this projekt that I don't want to push to github into a folder called gitignore
gitignore gitignore

24
AGENTS.md Normal file
View File

@@ -0,0 +1,24 @@
# Mize
Mize is many things.
- a "strongly typed filesystem for the age of everyone having many devices"
- a stdlib replacement
- a framework to make malleable/extensible/configurable/debugable software fast
- a OS userspace (mainly linux but later also any other kernels)
- a library for COP = Configuration Oriented Programming
- a thing providing one single API through which any data can be interacted with
All code written for, with or as part as mize (except for the small core) is written as a Mize Part. ALWAYS referr to:
@./docs/general-agent-guide.md
@./USING-MIZE-FOR-AGENTS.md
unless instructed otherwise put new parts into `./parts/vibed.<id-of-part>`.
## Codebase Structure
```
core/ # Core Mize framework (Rust)
parts/ # Mize parts (cdylibs / crates)
macros/ # Proc macros
docs/ # documentation
```

7046
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,10 +1,22 @@
[workspace] [workspace]
resolver = "2"
members = [ members = [
"packages/mize", "macros",
#"packages/vic", "apps/os",
"packages/ppc", "apps/obsidian-plugin",
"packages/marts", "parts/*",
"packages/ac_mize_macros", "parts/mize.ui/dioxus-components",
#"packages/mme",
] ]
exclude = [
"parts/mos",
# "parts/mme",
]
default-members = [
"apps/obsidian-plugin",
"apps/os",
"parts/*",
]
resolver = "2"
[workspace.dependencies]
mize = { path = "core" }

View File

@@ -1,25 +1,32 @@
# MiZe # MiZe
With Mize I am building the software platform, that I would want to use.
_Elevate the Unix file philosophy into the age of connectivity._ - website: https://mize.works
- source: https://git.ppc.social/ppc/mize
- matrix room: [#mize:ppc.social](https://matrix.to/#/#mize:ppc.social)
- matrix room for updates: [#mize-updates:ppc.social](https://matrix.to/#/#mize-updates:ppc.social)
## Why am I doing this? ## Mize is many things
- What annoys me about software these days is, that things are done in so many different ways. - a "strongly typed filesystem for the age of everyone having many devices"
- e.g: Different cloud applications for syncing data (Google Cloud, OneDrive, Dropbox) - "eelevate the UNIX philosophy into the age of having multiple connected devices"
- e.g: Every app stores the data it deals with in it's own way (the way that the developer thought is best for this particular app) - a new stdlib api to replace libc (but not just for C)
- a framework to make malleable/extensible/configurable/debugable software fast
- a OS userspace (mainly linux but later also any other kernels)
- a library for COP = Configuration Oriented Programming
- a thing providing one single API through which any data from any soruce can be interacted with
- a way to produce software with agents which is not slop
- (by making humans design architecture, agents implement code)
- (and providing a solid starter set of architecture for agents to use without humans)
- ([Mize Software Factory](https://wiki.ppc.social/Mize-Software-Factory))
- [c2vi](https://c2vi.dev) tinkering on the software he thinks is solid and would want to use
- Back when Unix came out **everything** was stored in one place: the file system of the computer. ## things
- (just as simple bytestreams/files, organized by a folder-structure) - [MiZe Parts](https://wiki.ppc.social/u/c2vi/MiZe-Part)
- and NO program stored and synced any data around in unpredictable ways. - [MiZe Init System](https://wiki.ppc.social/u/c2vi/MiZe-Init-System)
- [Config Oriented Programming|COP = Config Oriented Programming](https://wiki.ppc.social/u/c2vi/Config-Oriented-Programming)
- This philosophy is still used in every Unix/Linux machine, on a local level, but completely abstracted away (especially on mobile phones) from the user (by every app). - [mize lang](https://wiki.ppc.social/u/c2vi/mize-lang)
- [Mize Software Factory](https://wiki.ppc.social/Mize-Software-Factory)
- I would say: The Unix file system just hasn't evolved into the age of connectivity. It is still completely the same. And I think it shouldn't be.
- MiZe aims to bring that idea of storing all data
- in one place
- in one predictable and simple way
- and programs only taking data from there and not storing it everywhere in messy ways
- That's why the slogan is going to be: _Elevate the unix file philosophy into the age of connectivity._
- [The Idea of MiZe](https://wiki.ppc.social/u/c2vi/The-Idea-of-MiZe)
- [How the idea of MiZe came to be](https://wiki.ppc.social/u/c2vi/How-the-idea-of-MiZe-came-to-be)
- [The WHY of Mize](https://wiki.ppc.social/u/c2vi/The-WHY-of-Mize)
- [How MiZe works](https://wiki.ppc.social/u/c2vi/How-MiZe-works) (quite old article, probably no longer fully accurate)

72
USING-MIZE-FOR-AGENTS.md Normal file
View File

@@ -0,0 +1,72 @@
# The Mize software factory framework.
goal: make clean/sane, configurable, extensible, **maintanable**, **human-comprehendable**, debuggable, tracable, pluggable, **well-architectured**, ... software usuing agentic engineering.
@./docs/general-agent-guide.md
## Overview
Mize is built around the **parts architecture** which allows you to compose anc configure functionality modularly. Each part is a self-contained unit that:
- can store and manage its own state
- can expose methods to other parts
- Registers itself with the Mize instance
- Defines configuration options
- define UI components (called presenters)
Parts are the primary way to build any new software using this framework.
## conditions when to stop and check back in with the human (IMPORTANT).
- dealing with/adding dependencies on system libraries or packages is also something to stop for and ask the user how to do it.
- in case it takes too long finding smth or figuring smth out. stop and ask the user. (goal is not to do stuff as autonomous as possible)
- **Stop on architectural dead-ends.** If something does not work out architecture-wise — e.g. one part needs to notify another of a state change, but there is no elegant existing mechanism (`method` / `opt` / `has_part` / listeners / etc.) — do **not** invent a hacky workaround and push on. Stop, describe the gap and the options you see, and ask the user how to proceed.
## some other notes
- Prefer expanding an existing part over creating a new one when it fits — but ask user before expanding.
- when developing a new part. present it's config options and methods to the human first.
When turning a design / whole application into parts, follow **Splitting an app into parts**.
## Splitting an app into parts
A part is a **named capability with its own config namespace and callable surface** — not a technical layer (UI/API/DB) and not a folder dump of screens.
When turning a design document or whole application into parts:
1. **One capability, one part** — Group requirements that share one job and vocabulary (login/tokens, inbox, hub shell). That cluster becomes one part id (`auth.client`, `mme`, `sci.inbox`).
2. **Config namespace = part boundary** — Settings that naturally sit under one prefix (`auth.client.issuer`, …) belong to that part. Two unrelated prefixes → two parts.
3. **Methods are the public API** — Expose capability-shaped methods (`login`, `set_login_state`), not “whatever the dashboard clicked.” CLI and other parts should call the same `method`s via `dyn_call` / generated bindings — don’t use CLI `run` as the only inter-part API.
4. **Product / UI shells are thin** — Dashboards own layout and presentation. Domain work stays in capability parts. Optional UI notify via `has_part("…")` + `dyn_call` so headless/CLI still works.
5. **Split on replaceability or reuse** — Would you swap the IdP but keep the hub? Second app needs login without the hub? Keep that capability separate. Only this screen will ever use X? Keep X in the shell until a second consumer appears.
6. **Don’t split only by layer** — Avoid default `foo.ui` + `foo.logic`. Prefer `foo` (capability) + optional shell. Shared widgets can be a **library** (e.g. `dioxus-components`) until they need mize opts/methods.
7. **Hard vs soft dependency** — Hard: cannot run without the other (load order / deps). Soft: “if UI is loaded, push URL/code.” Soft deps must not break headless use.
8. **Lifecycle is a boundary smell** — Background refresh, blocking window, long device poll with different lifetimes often means the domain side should be its own part with clear methods.
9. **Start coarse; split on friction** — Prefer fewer parts from a design doc. Split when a second consumer appears, config namespaces diverge, or you need to enable one capability without another.
10. **Before coding, present a part table** — For each proposed part: `id`, one-sentence responsibility, `opt`s, `method`s, hard deps, soft notifies. Present that to the human first (same rule as presenting config options and methods).
## UI work
For any UI work:
- Use **Dioxus** (desktop unless the user says otherwise).
- Build with **`parts/mize.ui/dioxus-components`** — reuse existing components (`Button`, `Prompt`, `Control`, …) and extend that library when something reusable is missing; don’t invent a parallel widget set.
- Prefer **inlined CSS** via `OnceStyle` / isolated style components (see that crate). Do **not** put `document::Style` inside components that re-render often; don’t bring back `asset!` / css-modules for these components unless the user asks.
- Don’t block the Dioxus UI thread on long part work (login flows, etc.): background thread / async + part `method`s updating shared state the UI reads.
unless specified diferently any mize UI uses the PPC color pallete (derived from the PPC Logo: https://wiki.ppc.social/PPC-Logo):
```
Token Light mode Dark mode
Background #F7F8F4 #171D18
Surface #FFFFFF #242D25
Surface secondary #EFF2EA #2C362D
Primary text #20251C #F2F4EA
Secondary text #66705E #AAB5A4
Border #DFE4D9 #3A463A
Primary accent #FEC251 #D7E47D
Accent text #79500F #D7E47D
Success #526F32 #B9D987
Info #376777 #B9E4EB
Warning #A66B13 #FFD77F
Danger #B94D42 #FF9B8E
```

View File

@@ -0,0 +1,7 @@
[package]
name = "mize-obsidian-plugin"
version = "0.1.0"
edition = "2024"
[dependencies]
mize = { workspace = true }

View File

@@ -1,7 +1,7 @@
{ {
"name": "@ppc/obsidian-plugin", "name": "@ppc/obsidian-plugin",
"tasks": { "tasks": {
"build_rust": "wasm-pack -vvvvv build -d src/platform/obsidian/rust_dist --target web --no-default-features --features target-obsidian", "build_rust": "CARGO_TERM_COLOR=always wasm-pack -vvvvv build -d ../apps/obsidian-plugin/rust_dist --target web --no-opt ../../marts --no-default-features --features target-obsidian",
"build": "deno run -A build_rust && mkdir -p dist && touch dist/.hotreload && cp manifest.json dist && deno -A esbuild.config.mjs production", "build": "deno run -A build_rust && mkdir -p dist && touch dist/.hotreload && cp manifest.json dist && deno -A esbuild.config.mjs production",
"dev": "" "dev": ""
}, },

View File

@@ -1,36 +1,45 @@
{ {
"version": "5", "version": "5",
"specifiers": { "specifiers": {
"jsr:@deno/esbuild-plugin@^1.2.1": "1.2.1",
"jsr:@deno/loader@~0.3.10": "0.3.12",
"jsr:@std/internal@^1.0.12": "1.0.12",
"jsr:@std/path@^1.1.1": "1.1.4",
"npm:@flowershow/remark-wiki-link@^3.4.0": "3.4.0", "npm:@flowershow/remark-wiki-link@^3.4.0": "3.4.0",
"npm:@instantdb/admin@~0.21.3": "0.21.30",
"npm:@instantdb/react@~0.20.28": "0.20.28_react@19.1.0",
"npm:@react-oauth/google@~0.12.2": "0.12.2_react@19.1.0_react-dom@19.1.0__react@19.1.0",
"npm:@tailwindcss/postcss@4": "4.2.0",
"npm:@types/node@20": "20.19.33",
"npm:@types/react-dom@19": "19.2.3_@types+react@19.2.14",
"npm:@types/react@19": "19.2.14",
"npm:@wasm-tool/rollup-plugin-rust@^3.1.4": "3.1.4_binaryen@125.0.0",
"npm:binaryen@125": "125.0.0",
"npm:builtin-modules@5": "5.0.0", "npm:builtin-modules@5": "5.0.0",
"npm:esbuild@~0.27.3": "0.27.3", "npm:esbuild@~0.27.3": "0.27.3",
"npm:mdast@3": "3.0.0", "npm:mdast@3": "3.0.0",
"npm:obsidian@^1.12.2": "1.12.2_@codemirror+state@6.5.0_@codemirror+view@6.38.6", "npm:obsidian@^1.12.2": "1.12.2_@codemirror+state@6.5.0_@codemirror+view@6.38.6",
"npm:react-dom@19.1.0": "19.1.0_react@19.1.0",
"npm:react@19.1.0": "19.1.0",
"npm:remark-frontmatter@5": "5.0.0", "npm:remark-frontmatter@5": "5.0.0",
"npm:remark-gfm@^4.0.1": "4.0.1", "npm:remark-gfm@^4.0.1": "4.0.1",
"npm:remark-parse@11": "11.0.0", "npm:remark-parse@11": "11.0.0",
"npm:remark-stringify@11": "11.0.0", "npm:remark-stringify@11": "11.0.0",
"npm:tailwindcss@4": "4.2.0",
"npm:tslib@^2.6.2": "2.8.1",
"npm:typescript@^5.9.3": "5.9.3", "npm:typescript@^5.9.3": "5.9.3",
"npm:unified@^11.0.5": "11.0.5", "npm:unified@^11.0.5": "11.0.5",
"npm:unist-util-visit@^5.1.0": "5.1.0" "npm:unist-util-visit@^5.1.0": "5.1.0"
}, },
"npm": { "jsr": {
"@alloc/quick-lru@5.2.0": { "@deno/esbuild-plugin@1.2.1": {
"integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==" "integrity": "df629467913adc1f960149fdfa3a3430ba8c20381c310fba096db244e6c3c9f6",
"dependencies": [
"jsr:@deno/loader",
"jsr:@std/path"
]
}, },
"@deno/loader@0.3.12": {
"integrity": "52d3b3be0a32192efe07b0a4f1b3047077d2f2bba0f693e32f47421507f9fdb6"
},
"@std/internal@1.0.12": {
"integrity": "972a634fd5bc34b242024402972cd5143eac68d8dffaca5eaa4dba30ce17b027"
},
"@std/path@1.1.4": {
"integrity": "1d2d43f39efb1b42f0b1882a25486647cb851481862dc7313390b2bb044314b5",
"dependencies": [
"jsr:@std/internal"
]
}
},
"npm": {
"@codemirror/state@6.5.0": { "@codemirror/state@6.5.0": {
"integrity": "sha512-MwBHVK60IiIHDcoMet78lxt6iw5gJOGSbNbOIVBHWVXIH4/Nq1+GQgLLGgI1KlnN86WDXsPudVaqYHKBIx7Eyw==", "integrity": "sha512-MwBHVK60IiIHDcoMet78lxt6iw5gJOGSbNbOIVBHWVXIH4/Nq1+GQgLLGgI1KlnN86WDXsPudVaqYHKBIx7Eyw==",
"dependencies": [ "dependencies": [
@@ -185,196 +194,9 @@
"micromark-util-symbol" "micromark-util-symbol"
] ]
}, },
"@iarna/toml@2.2.5": {
"integrity": "sha512-trnsAYxU3xnS1gPHPyU961coFyLkh4gAD/0zQ5mymY4yOZ+CYvsPqUbOFSw0aDM4y0tV7tiFxL/1XfXPNC6IPg=="
},
"@instantdb/admin@0.21.30": {
"integrity": "sha512-IR8Wi+wJkGVqClJUnEFkUXYScyhmUV1qPO4s1+eHfFptkquHHtGSz1bUffGbGZJCj84lJVpjcvS1MCPgvd2PBg==",
"dependencies": [
"@instantdb/core@0.21.30",
"@instantdb/version@0.21.30",
"eventsource"
]
},
"@instantdb/core@0.20.28": {
"integrity": "sha512-0ePAgbOZedSLZ4LbPZL6afcFnhL5mWIoWXgOqzdmp6QUrYgtQjWrJCcFlA7DWmrkidZYLU2WVpQbhMbgzKqrOg==",
"dependencies": [
"@instantdb/version@0.20.28",
"mutative",
"uuid"
]
},
"@instantdb/core@0.21.30": {
"integrity": "sha512-PB/QFtrOYRmjKDYzo7oOIuJalLJVY/4mLljcYFkrbShMKz1oukKQ8sy+0/9pVcUETMyePgJUmuXcnrHwO7xjgQ==",
"dependencies": [
"@instantdb/version@0.21.30",
"mutative",
"uuid"
]
},
"@instantdb/react@0.20.28_react@19.1.0": {
"integrity": "sha512-wPig+jUeUqSCYGdCLIGVky8bL0h3VnjYYD2+5SZtF/KlgFGQZ+u8LCdvk+8QJL/kE0xqDbogr7D+b7Vn73uO/A==",
"dependencies": [
"@instantdb/core@0.20.28",
"@instantdb/version@0.20.28",
"react"
]
},
"@instantdb/version@0.20.28": {
"integrity": "sha512-lwsF66TpVwfBOSQ+bpuS2K1NO7jwlnJS5Uh4Xhyylwu4ieOEOOZa2vMnVORsidZCq2vwwKEiLvFIoH8lVkrj0g=="
},
"@instantdb/version@0.21.30": {
"integrity": "sha512-yhrxnZo3c2/EK3dnDEcX4XYHfXjlyj3QPPvylrBY2qxodKzcEh9BnJufiQiP+2ePUD+kET+2FQlztxQ4E9BAbg=="
},
"@isaacs/fs-minipass@4.0.1": {
"integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==",
"dependencies": [
"minipass"
]
},
"@jridgewell/gen-mapping@0.3.13": {
"integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==",
"dependencies": [
"@jridgewell/sourcemap-codec",
"@jridgewell/trace-mapping"
]
},
"@jridgewell/remapping@2.3.5": {
"integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==",
"dependencies": [
"@jridgewell/gen-mapping",
"@jridgewell/trace-mapping"
]
},
"@jridgewell/resolve-uri@3.1.2": {
"integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="
},
"@jridgewell/sourcemap-codec@1.5.5": {
"integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="
},
"@jridgewell/trace-mapping@0.3.31": {
"integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==",
"dependencies": [
"@jridgewell/resolve-uri",
"@jridgewell/sourcemap-codec"
]
},
"@marijn/find-cluster-break@1.0.2": { "@marijn/find-cluster-break@1.0.2": {
"integrity": "sha512-l0h88YhZFyKdXIFNfSWpyjStDjGHwZ/U7iobcK1cQQD8sejsONdQtTVU+1wVN1PBw40PiiHB1vA5S7VTfQiP9g==" "integrity": "sha512-l0h88YhZFyKdXIFNfSWpyjStDjGHwZ/U7iobcK1cQQD8sejsONdQtTVU+1wVN1PBw40PiiHB1vA5S7VTfQiP9g=="
}, },
"@react-oauth/google@0.12.2_react@19.1.0_react-dom@19.1.0__react@19.1.0": {
"integrity": "sha512-d1GVm2uD4E44EJft2RbKtp8Z1fp/gK8Lb6KHgs3pHlM0PxCXGLaq8LLYQYENnN4xPWO1gkL4apBtlPKzpLvZwg==",
"dependencies": [
"react",
"react-dom"
]
},
"@rollup/pluginutils@5.3.0": {
"integrity": "sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q==",
"dependencies": [
"@types/estree",
"estree-walker",
"picomatch"
]
},
"@tailwindcss/node@4.2.0": {
"integrity": "sha512-Yv+fn/o2OmL5fh/Ir62VXItdShnUxfpkMA4Y7jdeC8O81WPB8Kf6TT6GSHvnqgSwDzlB5iT7kDpeXxLsUS0T6Q==",
"dependencies": [
"@jridgewell/remapping",
"enhanced-resolve",
"jiti",
"lightningcss",
"magic-string",
"source-map-js",
"tailwindcss"
]
},
"@tailwindcss/oxide-android-arm64@4.2.0": {
"integrity": "sha512-F0QkHAVaW/JNBWl4CEKWdZ9PMb0khw5DCELAOnu+RtjAfx5Zgw+gqCHFvqg3AirU1IAd181fwOtJQ5I8Yx5wtw==",
"os": ["android"],
"cpu": ["arm64"]
},
"@tailwindcss/oxide-darwin-arm64@4.2.0": {
"integrity": "sha512-I0QylkXsBsJMZ4nkUNSR04p6+UptjcwhcVo3Zu828ikiEqHjVmQL9RuQ6uT/cVIiKpvtVA25msu/eRV97JeNSA==",
"os": ["darwin"],
"cpu": ["arm64"]
},
"@tailwindcss/oxide-darwin-x64@4.2.0": {
"integrity": "sha512-6TmQIn4p09PBrmnkvbYQ0wbZhLtbaksCDx7Y7R3FYYx0yxNA7xg5KP7dowmQ3d2JVdabIHvs3Hx4K3d5uCf8xg==",
"os": ["darwin"],
"cpu": ["x64"]
},
"@tailwindcss/oxide-freebsd-x64@4.2.0": {
"integrity": "sha512-qBudxDvAa2QwGlq9y7VIzhTvp2mLJ6nD/G8/tI70DCDoneaUeLWBJaPcbfzqRIWraj+o969aDQKvKW9dvkUizw==",
"os": ["freebsd"],
"cpu": ["x64"]
},
"@tailwindcss/oxide-linux-arm-gnueabihf@4.2.0": {
"integrity": "sha512-7XKkitpy5NIjFZNUQPeUyNJNJn1CJeV7rmMR+exHfTuOsg8rxIO9eNV5TSEnqRcaOK77zQpsyUkBWmPy8FgdSg==",
"os": ["linux"],
"cpu": ["arm"]
},
"@tailwindcss/oxide-linux-arm64-gnu@4.2.0": {
"integrity": "sha512-Mff5a5Q3WoQR01pGU1gr29hHM1N93xYrKkGXfPw/aRtK4bOc331Ho4Tgfsm5WDGvpevqMpdlkCojT3qlCQbCpA==",
"os": ["linux"],
"cpu": ["arm64"]
},
"@tailwindcss/oxide-linux-arm64-musl@4.2.0": {
"integrity": "sha512-XKcSStleEVnbH6W/9DHzZv1YhjE4eSS6zOu2eRtYAIh7aV4o3vIBs+t/B15xlqoxt6ef/0uiqJVB6hkHjWD/0A==",
"os": ["linux"],
"cpu": ["arm64"]
},
"@tailwindcss/oxide-linux-x64-gnu@4.2.0": {
"integrity": "sha512-/hlXCBqn9K6fi7eAM0RsobHwJYa5V/xzWspVTzxnX+Ft9v6n+30Pz8+RxCn7sQL/vRHHLS30iQPrHQunu6/vJA==",
"os": ["linux"],
"cpu": ["x64"]
},
"@tailwindcss/oxide-linux-x64-musl@4.2.0": {
"integrity": "sha512-lKUaygq4G7sWkhQbfdRRBkaq4LY39IriqBQ+Gk6l5nKq6Ay2M2ZZb1tlIyRNgZKS8cbErTwuYSor0IIULC0SHw==",
"os": ["linux"],
"cpu": ["x64"]
},
"@tailwindcss/oxide-wasm32-wasi@4.2.0": {
"integrity": "sha512-xuDjhAsFdUuFP5W9Ze4k/o4AskUtI8bcAGU4puTYprr89QaYFmhYOPfP+d1pH+k9ets6RoE23BXZM1X1jJqoyw==",
"cpu": ["wasm32"]
},
"@tailwindcss/oxide-win32-arm64-msvc@4.2.0": {
"integrity": "sha512-2UU/15y1sWDEDNJXxEIrfWKC2Yb4YgIW5Xz2fKFqGzFWfoMHWFlfa1EJlGO2Xzjkq/tvSarh9ZTjvbxqWvLLXA==",
"os": ["win32"],
"cpu": ["arm64"]
},
"@tailwindcss/oxide-win32-x64-msvc@4.2.0": {
"integrity": "sha512-CrFadmFoc+z76EV6LPG1jx6XceDsaCG3lFhyLNo/bV9ByPrE+FnBPckXQVP4XRkN76h3Fjt/a+5Er/oA/nCBvQ==",
"os": ["win32"],
"cpu": ["x64"]
},
"@tailwindcss/oxide@4.2.0": {
"integrity": "sha512-AZqQzADaj742oqn2xjl5JbIOzZB/DGCYF/7bpvhA8KvjUj9HJkag6bBuwZvH1ps6dfgxNHyuJVlzSr2VpMgdTQ==",
"optionalDependencies": [
"@tailwindcss/oxide-android-arm64",
"@tailwindcss/oxide-darwin-arm64",
"@tailwindcss/oxide-darwin-x64",
"@tailwindcss/oxide-freebsd-x64",
"@tailwindcss/oxide-linux-arm-gnueabihf",
"@tailwindcss/oxide-linux-arm64-gnu",
"@tailwindcss/oxide-linux-arm64-musl",
"@tailwindcss/oxide-linux-x64-gnu",
"@tailwindcss/oxide-linux-x64-musl",
"@tailwindcss/oxide-wasm32-wasi",
"@tailwindcss/oxide-win32-arm64-msvc",
"@tailwindcss/oxide-win32-x64-msvc"
]
},
"@tailwindcss/postcss@4.2.0": {
"integrity": "sha512-u6YBacGpOm/ixPfKqfgrJEjMfrYmPD7gEFRoygS/hnQaRtV0VCBdpkx5Ouw9pnaLRwwlgGCuJw8xLpaR0hOrQg==",
"dependencies": [
"@alloc/quick-lru",
"@tailwindcss/node",
"@tailwindcss/oxide",
"postcss",
"tailwindcss"
]
},
"@types/codemirror@5.60.8": { "@types/codemirror@5.60.8": {
"integrity": "sha512-VjFgDF/eB+Aklcy15TtOTLQeMjTo07k7KAjql8OK5Dirr7a6sJY4T1uVBDuTVG9VEmn1uUsohOpYnVfgC6/jyw==", "integrity": "sha512-VjFgDF/eB+Aklcy15TtOTLQeMjTo07k7KAjql8OK5Dirr7a6sJY4T1uVBDuTVG9VEmn1uUsohOpYnVfgC6/jyw==",
"dependencies": [ "dependencies": [
@@ -399,24 +221,6 @@
"@types/ms@2.1.0": { "@types/ms@2.1.0": {
"integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==" "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA=="
}, },
"@types/node@20.19.33": {
"integrity": "sha512-Rs1bVAIdBs5gbTIKza/tgpMuG1k3U/UMJLWecIMxNdJFDMzcM5LOiLVRYh3PilWEYDIeUDv7bpiHPLPsbydGcw==",
"dependencies": [
"undici-types"
]
},
"@types/react-dom@19.2.3_@types+react@19.2.14": {
"integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==",
"dependencies": [
"@types/react"
]
},
"@types/react@19.2.14": {
"integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==",
"dependencies": [
"csstype"
]
},
"@types/tern@0.23.9": { "@types/tern@0.23.9": {
"integrity": "sha512-ypzHFE/wBzh+BlH6rrBgS5I/Z7RD21pGhZ2rltb/+ZrVM1awdZwjx7hE5XfuYgHWk9uvV5HLZN3SloevCAp3Bw==", "integrity": "sha512-ypzHFE/wBzh+BlH6rrBgS5I/Z7RD21pGhZ2rltb/+ZrVM1awdZwjx7hE5XfuYgHWk9uvV5HLZN3SloevCAp3Bw==",
"dependencies": [ "dependencies": [
@@ -426,59 +230,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=="
}, },
"@wasm-tool/rollup-plugin-rust@3.1.4_binaryen@125.0.0": {
"integrity": "sha512-XW3nk8yFPPc86jLXVEXkqQxsx9M6i+j5Boru5yapfS/+egK2IOYsuEOvM3am+/YrKczohKP45+cBKYkxSSGGzg==",
"dependencies": [
"@iarna/toml",
"@rollup/pluginutils",
"binaryen",
"chalk",
"glob",
"node-fetch",
"rimraf",
"tar"
]
},
"bail@2.0.2": { "bail@2.0.2": {
"integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==" "integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw=="
}, },
"balanced-match@4.0.3": {
"integrity": "sha512-1pHv8LX9CpKut1Zp4EXey7Z8OfH11ONNH6Dhi2WDUt31VVZFXZzKwXcysBgqSumFCmR+0dqjMK5v5JiFHzi0+g=="
},
"binaryen@125.0.0": {
"integrity": "sha512-X7CUM9ZnwL/Ow++JH5AJKiemc82J7JyeryuPvXQdXBLcL/rqrC5KMUB1mHiORSolietH9sotvaOZlr6HSwPAlw==",
"bin": true
},
"brace-expansion@5.0.2": {
"integrity": "sha512-Pdk8c9poy+YhOgVWw1JNN22/HcivgKWwpxKq04M/jTmHyCZn12WPJebZxdjSa5TmBqISrUSgNYU3eRORljfCCw==",
"dependencies": [
"balanced-match"
]
},
"builtin-modules@5.0.0": { "builtin-modules@5.0.0": {
"integrity": "sha512-bkXY9WsVpY7CvMhKSR6pZilZu9Ln5WDrKVBUXf2S443etkmEO4V58heTecXcUIsNsi4Rx8JUO4NfX1IcQl4deg==" "integrity": "sha512-bkXY9WsVpY7CvMhKSR6pZilZu9Ln5WDrKVBUXf2S443etkmEO4V58heTecXcUIsNsi4Rx8JUO4NfX1IcQl4deg=="
}, },
"ccount@2.0.1": { "ccount@2.0.1": {
"integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==" "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg=="
}, },
"chalk@5.6.2": {
"integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA=="
},
"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=="
}, },
"chownr@3.0.0": {
"integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g=="
},
"crelt@1.0.6": { "crelt@1.0.6": {
"integrity": "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==" "integrity": "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g=="
}, },
"csstype@3.2.3": {
"integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ=="
},
"data-uri-to-buffer@4.0.1": {
"integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A=="
},
"debug@4.4.3": { "debug@4.4.3": {
"integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
"dependencies": [ "dependencies": [
@@ -494,22 +260,12 @@
"dequal@2.0.3": { "dequal@2.0.3": {
"integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==" "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA=="
}, },
"detect-libc@2.1.2": {
"integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="
},
"devlop@1.1.0": { "devlop@1.1.0": {
"integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==", "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==",
"dependencies": [ "dependencies": [
"dequal" "dequal"
] ]
}, },
"enhanced-resolve@5.19.0": {
"integrity": "sha512-phv3E1Xl4tQOShqSte26C7Fl84EwUdZsyOuSSk9qtAGyyQs2s3jJzComh+Abf4g187lUUAvH+H26omrqia2aGg==",
"dependencies": [
"graceful-fs",
"tapable"
]
},
"esbuild@0.27.3": { "esbuild@0.27.3": {
"integrity": "sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==", "integrity": "sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==",
"optionalDependencies": [ "optionalDependencies": [
@@ -546,18 +302,6 @@
"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=="
}, },
"estree-walker@2.0.2": {
"integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="
},
"eventsource-parser@3.0.6": {
"integrity": "sha512-Vo1ab+QXPzZ4tCa8SwIHJFaSzy4R6SHf7BY79rFBDf0idraZWAkYrDjDj8uWaSm3S2TK+hJ7/t1CEmZ7jXw+pg=="
},
"eventsource@4.1.0": {
"integrity": "sha512-2GuF51iuHX6A9xdTccMTsNb7VO0lHZihApxhvQzJB5A03DvHDd2FQepodbMaztPBmBcE/ox7o2gqaxGhYB9LhQ==",
"dependencies": [
"eventsource-parser"
]
},
"extend@3.0.2": { "extend@3.0.2": {
"integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="
}, },
@@ -567,129 +311,18 @@
"format" "format"
] ]
}, },
"fetch-blob@3.2.0": {
"integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==",
"dependencies": [
"node-domexception",
"web-streams-polyfill"
]
},
"format@0.2.2": { "format@0.2.2": {
"integrity": "sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==" "integrity": "sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww=="
}, },
"formdata-polyfill@4.0.10": {
"integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==",
"dependencies": [
"fetch-blob"
]
},
"github-slugger@2.0.0": { "github-slugger@2.0.0": {
"integrity": "sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw==" "integrity": "sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw=="
}, },
"glob@13.0.6": {
"integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==",
"dependencies": [
"minimatch",
"minipass",
"path-scurry"
]
},
"graceful-fs@4.2.11": {
"integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="
},
"is-plain-obj@4.1.0": { "is-plain-obj@4.1.0": {
"integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==" "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg=="
}, },
"jiti@2.6.1": {
"integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==",
"bin": true
},
"lightningcss-android-arm64@1.31.1": {
"integrity": "sha512-HXJF3x8w9nQ4jbXRiNppBCqeZPIAfUo8zE/kOEGbW5NZvGc/K7nMxbhIr+YlFlHW5mpbg/YFPdbnCh1wAXCKFg==",
"os": ["android"],
"cpu": ["arm64"]
},
"lightningcss-darwin-arm64@1.31.1": {
"integrity": "sha512-02uTEqf3vIfNMq3h/z2cJfcOXnQ0GRwQrkmPafhueLb2h7mqEidiCzkE4gBMEH65abHRiQvhdcQ+aP0D0g67sg==",
"os": ["darwin"],
"cpu": ["arm64"]
},
"lightningcss-darwin-x64@1.31.1": {
"integrity": "sha512-1ObhyoCY+tGxtsz1lSx5NXCj3nirk0Y0kB/g8B8DT+sSx4G9djitg9ejFnjb3gJNWo7qXH4DIy2SUHvpoFwfTA==",
"os": ["darwin"],
"cpu": ["x64"]
},
"lightningcss-freebsd-x64@1.31.1": {
"integrity": "sha512-1RINmQKAItO6ISxYgPwszQE1BrsVU5aB45ho6O42mu96UiZBxEXsuQ7cJW4zs4CEodPUioj/QrXW1r9pLUM74A==",
"os": ["freebsd"],
"cpu": ["x64"]
},
"lightningcss-linux-arm-gnueabihf@1.31.1": {
"integrity": "sha512-OOCm2//MZJ87CdDK62rZIu+aw9gBv4azMJuA8/KB74wmfS3lnC4yoPHm0uXZ/dvNNHmnZnB8XLAZzObeG0nS1g==",
"os": ["linux"],
"cpu": ["arm"]
},
"lightningcss-linux-arm64-gnu@1.31.1": {
"integrity": "sha512-WKyLWztD71rTnou4xAD5kQT+982wvca7E6QoLpoawZ1gP9JM0GJj4Tp5jMUh9B3AitHbRZ2/H3W5xQmdEOUlLg==",
"os": ["linux"],
"cpu": ["arm64"]
},
"lightningcss-linux-arm64-musl@1.31.1": {
"integrity": "sha512-mVZ7Pg2zIbe3XlNbZJdjs86YViQFoJSpc41CbVmKBPiGmC4YrfeOyz65ms2qpAobVd7WQsbW4PdsSJEMymyIMg==",
"os": ["linux"],
"cpu": ["arm64"]
},
"lightningcss-linux-x64-gnu@1.31.1": {
"integrity": "sha512-xGlFWRMl+0KvUhgySdIaReQdB4FNudfUTARn7q0hh/V67PVGCs3ADFjw+6++kG1RNd0zdGRlEKa+T13/tQjPMA==",
"os": ["linux"],
"cpu": ["x64"]
},
"lightningcss-linux-x64-musl@1.31.1": {
"integrity": "sha512-eowF8PrKHw9LpoZii5tdZwnBcYDxRw2rRCyvAXLi34iyeYfqCQNA9rmUM0ce62NlPhCvof1+9ivRaTY6pSKDaA==",
"os": ["linux"],
"cpu": ["x64"]
},
"lightningcss-win32-arm64-msvc@1.31.1": {
"integrity": "sha512-aJReEbSEQzx1uBlQizAOBSjcmr9dCdL3XuC/6HLXAxmtErsj2ICo5yYggg1qOODQMtnjNQv2UHb9NpOuFtYe4w==",
"os": ["win32"],
"cpu": ["arm64"]
},
"lightningcss-win32-x64-msvc@1.31.1": {
"integrity": "sha512-I9aiFrbd7oYHwlnQDqr1Roz+fTz61oDDJX7n9tYF9FJymH1cIN1DtKw3iYt6b8WZgEjoNwVSncwF4wx/ZedMhw==",
"os": ["win32"],
"cpu": ["x64"]
},
"lightningcss@1.31.1": {
"integrity": "sha512-l51N2r93WmGUye3WuFoN5k10zyvrVs0qfKBhyC5ogUQ6Ew6JUSswh78mbSO+IU3nTWsyOArqPCcShdQSadghBQ==",
"dependencies": [
"detect-libc"
],
"optionalDependencies": [
"lightningcss-android-arm64",
"lightningcss-darwin-arm64",
"lightningcss-darwin-x64",
"lightningcss-freebsd-x64",
"lightningcss-linux-arm-gnueabihf",
"lightningcss-linux-arm64-gnu",
"lightningcss-linux-arm64-musl",
"lightningcss-linux-x64-gnu",
"lightningcss-linux-x64-musl",
"lightningcss-win32-arm64-msvc",
"lightningcss-win32-x64-msvc"
]
},
"longest-streak@3.1.0": { "longest-streak@3.1.0": {
"integrity": "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==" "integrity": "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g=="
}, },
"lru-cache@11.2.6": {
"integrity": "sha512-ESL2CrkS/2wTPfuend7Zhkzo2u0daGJ/A2VucJOgQ/C48S/zB8MMeMHSGKYpXhIjbPxfuezITkaBH1wqv00DDQ=="
},
"magic-string@0.30.21": {
"integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==",
"dependencies": [
"@jridgewell/sourcemap-codec"
]
},
"markdown-table@3.0.4": { "markdown-table@3.0.4": {
"integrity": "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==" "integrity": "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw=="
}, },
@@ -1070,46 +703,12 @@
"micromark-util-types" "micromark-util-types"
] ]
}, },
"minimatch@10.2.2": {
"integrity": "sha512-+G4CpNBxa5MprY+04MbgOw1v7So6n5JY166pFi9KfYwT78fxScCeSNQSNzp6dpPSW2rONOps6Ocam1wFhCgoVw==",
"dependencies": [
"brace-expansion"
]
},
"minipass@7.1.3": {
"integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A=="
},
"minizlib@3.1.0": {
"integrity": "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==",
"dependencies": [
"minipass"
]
},
"moment@2.29.4": { "moment@2.29.4": {
"integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==" "integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w=="
}, },
"ms@2.1.3": { "ms@2.1.3": {
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
}, },
"mutative@1.3.0": {
"integrity": "sha512-8MJj6URmOZAV70dpFe1YnSppRTKC4DsMkXQiBDFayLcDI4ljGokHxmpqaBQuDWa4iAxWaJJ1PS8vAmbntjjKmQ=="
},
"nanoid@3.3.11": {
"integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
"bin": true
},
"node-domexception@1.0.0": {
"integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==",
"deprecated": true
},
"node-fetch@3.3.2": {
"integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==",
"dependencies": [
"data-uri-to-buffer",
"fetch-blob",
"formdata-polyfill"
]
},
"obsidian@1.12.2_@codemirror+state@6.5.0_@codemirror+view@6.38.6": { "obsidian@1.12.2_@codemirror+state@6.5.0_@codemirror+view@6.38.6": {
"integrity": "sha512-DGAzpt6vo+sMDET/o8Zj26Bj2hbHrFsNUU8TtnzCyerO/OHksMFQnU9QEnPHVsOMstt/WnHXfC56j2r1syObWg==", "integrity": "sha512-DGAzpt6vo+sMDET/o8Zj26Bj2hbHrFsNUU8TtnzCyerO/OHksMFQnU9QEnPHVsOMstt/WnHXfC56j2r1syObWg==",
"dependencies": [ "dependencies": [
@@ -1119,40 +718,6 @@
"moment" "moment"
] ]
}, },
"package-json-from-dist@1.0.1": {
"integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw=="
},
"path-scurry@2.0.2": {
"integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==",
"dependencies": [
"lru-cache",
"minipass"
]
},
"picocolors@1.1.1": {
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="
},
"picomatch@4.0.3": {
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="
},
"postcss@8.5.6": {
"integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==",
"dependencies": [
"nanoid",
"picocolors",
"source-map-js"
]
},
"react-dom@19.1.0_react@19.1.0": {
"integrity": "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==",
"dependencies": [
"react",
"scheduler"
]
},
"react@19.1.0": {
"integrity": "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg=="
},
"remark-frontmatter@5.0.0": { "remark-frontmatter@5.0.0": {
"integrity": "sha512-XTFYvNASMe5iPN0719nPrdItC9aU0ssC4v14mH1BCi1u0n1gAocqcujWUrByftZTbLhRtiKRyjYTSIOcr69UVQ==", "integrity": "sha512-XTFYvNASMe5iPN0719nPrdItC9aU0ssC4v14mH1BCi1u0n1gAocqcujWUrByftZTbLhRtiKRyjYTSIOcr69UVQ==",
"dependencies": [ "dependencies": [
@@ -1190,52 +755,16 @@
"unified" "unified"
] ]
}, },
"rimraf@6.1.3": {
"integrity": "sha512-LKg+Cr2ZF61fkcaK1UdkH2yEBBKnYjTyWzTJT6KNPcSPaiT7HSdhtMXQuN5wkTX0Xu72KQ1l8S42rlmexS2hSA==",
"dependencies": [
"glob",
"package-json-from-dist"
],
"bin": true
},
"scheduler@0.26.0": {
"integrity": "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA=="
},
"source-map-js@1.2.1": {
"integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="
},
"style-mod@4.1.3": { "style-mod@4.1.3": {
"integrity": "sha512-i/n8VsZydrugj3Iuzll8+x/00GH2vnYsk1eomD8QiRrSAeW6ItbCQDtfXCeJHd0iwiNagqjQkvpvREEPtW3IoQ==" "integrity": "sha512-i/n8VsZydrugj3Iuzll8+x/00GH2vnYsk1eomD8QiRrSAeW6ItbCQDtfXCeJHd0iwiNagqjQkvpvREEPtW3IoQ=="
}, },
"tailwindcss@4.2.0": {
"integrity": "sha512-yYzTZ4++b7fNYxFfpnberEEKu43w44aqDMNM9MHMmcKuCH7lL8jJ4yJ7LGHv7rSwiqM0nkiobF9I6cLlpS2P7Q=="
},
"tapable@2.3.0": {
"integrity": "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg=="
},
"tar@7.5.9": {
"integrity": "sha512-BTLcK0xsDh2+PUe9F6c2TlRp4zOOBMTkoQHQIWSIzI0R7KG46uEwq4OPk2W7bZcprBMsuaeFsqwYr7pjh6CuHg==",
"dependencies": [
"@isaacs/fs-minipass",
"chownr",
"minipass",
"minizlib",
"yallist"
]
},
"trough@2.2.0": { "trough@2.2.0": {
"integrity": "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==" "integrity": "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw=="
}, },
"tslib@2.8.1": {
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="
},
"typescript@5.9.3": { "typescript@5.9.3": {
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
"bin": true "bin": true
}, },
"undici-types@6.21.0": {
"integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="
},
"unified@11.0.5": { "unified@11.0.5": {
"integrity": "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==", "integrity": "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==",
"dependencies": [ "dependencies": [
@@ -1275,10 +804,6 @@
"unist-util-visit-parents" "unist-util-visit-parents"
] ]
}, },
"uuid@11.1.0": {
"integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==",
"bin": true
},
"vfile-message@4.0.3": { "vfile-message@4.0.3": {
"integrity": "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==", "integrity": "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==",
"dependencies": [ "dependencies": [
@@ -1296,45 +821,25 @@
"w3c-keyname@2.2.8": { "w3c-keyname@2.2.8": {
"integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==" "integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ=="
}, },
"web-streams-polyfill@3.3.3": {
"integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw=="
},
"yallist@5.0.0": {
"integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw=="
},
"zwitch@2.0.4": { "zwitch@2.0.4": {
"integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==" "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A=="
} }
}, },
"workspace": { "workspace": {
"packageJson": {
"dependencies": [ "dependencies": [
"jsr:@deno/esbuild-plugin@^1.2.1",
"npm:@flowershow/remark-wiki-link@^3.4.0", "npm:@flowershow/remark-wiki-link@^3.4.0",
"npm:@instantdb/admin@~0.21.3",
"npm:@instantdb/react@~0.20.28",
"npm:@react-oauth/google@~0.12.2",
"npm:@tailwindcss/postcss@4",
"npm:@types/node@20",
"npm:@types/react-dom@19",
"npm:@types/react@19",
"npm:@wasm-tool/rollup-plugin-rust@^3.1.4",
"npm:binaryen@125",
"npm:builtin-modules@5", "npm:builtin-modules@5",
"npm:esbuild@~0.27.3", "npm:esbuild@~0.27.3",
"npm:mdast@3", "npm:mdast@3",
"npm:obsidian@^1.12.2", "npm:obsidian@^1.12.2",
"npm:react-dom@19.1.0",
"npm:react@19.1.0",
"npm:remark-frontmatter@5", "npm:remark-frontmatter@5",
"npm:remark-gfm@^4.0.1", "npm:remark-gfm@^4.0.1",
"npm:remark-parse@11", "npm:remark-parse@11",
"npm:remark-stringify@11", "npm:remark-stringify@11",
"npm:tailwindcss@4",
"npm:tslib@^2.6.2",
"npm:typescript@^5.9.3", "npm:typescript@^5.9.3",
"npm:unified@^11.0.5", "npm:unified@^11.0.5",
"npm:unist-util-visit@^5.1.0" "npm:unist-util-visit@^5.1.0"
] ]
} }
}
} }

View 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"
}

View 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(())
}

View File

@@ -0,0 +1,173 @@
import { Plugin, ItemView, TFile, Modal } from "obsidian";
import wasmBinary from "./rust_dist/marts_bg.wasm";
import * as wasmNamespace from "./rust_dist/marts";
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/marts.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();
}
}

View File

@@ -0,0 +1,3 @@
fn main() {
println!("Hello, world!");
}

13
apps/os/Cargo.toml Normal file
View File

@@ -0,0 +1,13 @@
[package]
name = "mize-bin"
version = "0.1.0"
edition = "2024"
[dependencies]
mize = { workspace = true }
clap = { version = "4.5.59", features = ["string"] }
tracing = "0.1.44"
[[bin]]
name = "mize"
path = "./src/main.rs"

161
apps/os/src/cli.rs Normal file
View File

@@ -0,0 +1,161 @@
use clap::ArgMatches;
use core::result::Result::Err;
use core::unimplemented;
use mize::flume::bounded;
use mize::{instance, mize_err};
use std::ffi::OsString;
use std::io::Read;
use std::path::PathBuf;
use std::str::FromStr;
use tracing::debug;
use mize::ciborium;
use mize::error::{IntoMizeResult, MizeError, MizeResult, MizeResultTrait};
use mize::instance::subscription::Subscription;
use mize::instance::subscription::Update;
use mize::instance::Mize;
use mize::item::{IntoItemData, ItemData};
use mize::platform::os::config_from_cli_args;
use mize::platform::os::fsstore::FileStore;
use crate::generated;
pub fn get(sub_matches: &ArgMatches, instance: &mut Mize) -> MizeResult<()> {
let id = sub_matches
.get_one::<String>("id")
.ok_or(MizeError::new().msg("No id Argument specified"))?;
let raw = sub_matches.get_flag("raw");
let data = instance.get_config(id)?;
if raw {
print!("{}", data.value_string()?);
} else {
println!("{}", data);
}
Ok(())
}
pub fn call(sub_matches: &ArgMatches, instance: &mut Mize) -> MizeResult<()> {
println!("call cmd not implemented");
Ok(())
}
pub fn create(sub_matches: &ArgMatches, instance: &mut Mize) -> MizeResult<()> {
let item = instance.new_item()?;
println!("id: {}", item.id());
println!("with namespace: {}", item.id().namespace().as_real_string());
return Ok(());
}
pub fn status(sub_matches: &ArgMatches, instance: &mut Mize) -> MizeResult<()> {
println!("not implemented");
Err(mize_err!("not implemented"))
}
pub fn info(sub_matches: &ArgMatches, instance: &mut Mize) -> MizeResult<()> {
println!("not implemented");
Err(mize_err!("not implemented"))
}
pub fn run(sub_matches: &ArgMatches, mize: &mut Mize) -> MizeResult<()> {
match sub_matches.subcommand() {
None => { // run a mize instance (does whatever it's config will say)
mize.wait();
}
Some((runnable, sub_matches)) => { // run smth from a part. for now registered cli commands
let mut ext_args: Vec<String> = sub_matches
.get_many::<OsString>("")
.unwrap_or_default()
.map(|v| v.to_string_lossy().to_string())
.collect();
let mut ext_cmdline = vec![runnable.to_owned()];
ext_cmdline.append(&mut ext_args);
let mut cli = generated::CliPart::get("cli")?;
cli.run(ext_cmdline)?;
}
}
Ok(())
}
pub fn set(sub_matches: &ArgMatches, instance: &mut Mize) -> MizeResult<()> {
let id = sub_matches
.get_one::<String>("id")
.ok_or(MizeError::new().msg("No id Argument specified"))?;
let value = sub_matches
.get_one::<String>("value")
.ok_or(MizeError::new().msg("No value Argument specified"))?;
instance.set_blocking(id, value.into_item_data())?;
Ok(())
}
pub fn show(sub_matches: &ArgMatches, instance: &mut Mize) -> MizeResult<()> {
println!("not implemented");
// let instance = Mize::with_config(config_from_cli_args(sub_matches)?)?;
// let id = sub_matches
// .get_one::<String>("id")
// .ok_or(MizeError::new().msg("No id Argument specified"))?;
// let item = instance.get(id)?;
// let (tx, rx) = bounded::<Update>(4);
// let sub = Subscription::from_sender(tx);
// instance.sub(id, sub)?;
// println!("item: {}", item.as_data_full()?);
// for update in rx {
// println!("####### GOT UPDATE #######");
// println!("item: {}", update.new_item()?.as_data_full()?);
// }
Ok(())
}
pub fn format_cbor(sub_matches: &ArgMatches, instance: &mut Mize) -> MizeResult<()> {
let mut input: Vec<u8> = Vec::new();
let stdin = std::io::stdin();
let mut handle = stdin.lock();
//handle.read_to_end(&mut input);
let cbor_data = ciborium::from_reader(handle)?;
let item_data = ItemData::from_cbor(cbor_data);
println!("{}", item_data);
Ok(())
}
pub fn run_from_part(cmd: &str, sub_matches: &ArgMatches, instance: &mut Mize) -> MizeResult<()> {
let default = OsString::default();
let mut sub_cmd_line: Vec<OsString> = sub_matches
.get_many::<OsString>("")
.unwrap()
.map(|e| e.to_owned())
.collect();
sub_cmd_line.insert(0, OsString::from_str(cmd)?);
debug!("loading module '{}'", cmd);
instance.load_part(cmd)?;
println!("running cli from part not implemented");
//instance
//.get_part(cmd)?
//.run_cli(&instance, sub_cmd_line)
//.ok_or(mize_err!(
//"The Part '{cmd}' does not implement the run_cli() functionality"
//))??;
Ok(())
}

70
apps/os/src/generated.rs Normal file
View File

@@ -0,0 +1,70 @@
use mize::prelude::*;
pub struct CliPart {
id: MizeId
}
impl MizePart for CliPart {
fn id(&self) -> MizeResult<MizeId> {
Ok(self.id.clone())
}
}
impl CliPart {
pub fn get(name: &str) -> MizeResult<CliPart> {
let mut mize = get_global_mize()?;
let part = mize.get_part(name)?;
Ok(CliPart { id: part.id()? })
}
pub fn run(&self, cmdline: Vec<String>) -> MizeResult<()> {
let cmdline = cmdline.into_item_data();
let mut args = ItemData::new();
args.set_path("0", cmdline)?;
dyn_call(self.id.clone(), "run", args)?;
Ok(())
}
pub fn set_command(&self, name: &str) -> MizeResult<()> {
let name = name.into_item_data();
let mut args = ItemData::new();
args.set_path("0", name)?;
dyn_call(self.id.clone(), "set_command", args)?;
Ok(())
}
pub fn subcommand<T: Fn(ArgMatches) -> MizeResult<()> + Send + Sync + 'static>(&self, subcmd: Command, action: T) -> MizeResult<()> {
let mut subcmd_arg = ItemData::new();
subcmd_arg.set_path("_native", subcmd.native_id)?;
let mut action_arg = ItemData::from_native(action)?;
let mut args = ItemData::new();
args.set_path("0", subcmd_arg)?;
args.set_path("1", action_arg)?;
dyn_call(self.id.clone(), "subcommand", args)?;
Ok(())
}
}
// the clap::Command native stuff
pub struct Command {
native_id: u32
}
pub struct ArgMatches {
native_id: u32
}
pub struct Action {
native_id: u32
}
impl Command {
pub fn new(name: &str) -> MizeResult<Command> {
let name = name.into_item_data();
let mut args = ItemData::new();
args.set_path("0", name)?;
let mut mize = get_global_mize()?;
let id = mize.id_new_dots("cli.Command")?;
let data = dyn_call(id.clone(), "new", args)?;
let native_id: u32 = data.get_path("_native")?.native_take()?;
Ok(Command { native_id })
}
}

177
apps/os/src/main.rs Normal file
View File

@@ -0,0 +1,177 @@
#![allow(warnings)]
use clap::builder::OsStr;
use clap::{crate_version, Arg, Command};
use clap::{ArgAction, ArgMatches};
use std::ffi::OsString;
use std::path::PathBuf;
use std::process::exit;
use mize::error::{MizeError, MizeResult};
use mize::item::ItemData;
use mize::platform::os::config_from_cli_args;
use mize::platform::os::logging::init_logger;
use mize::{Mize, instance, mize_err};
use std::env;
use std::io::Write;
use std::sync::Arc;
use tracing::{debug, error, info, trace, warn};
mod cli;
// the generated module by hand. as we need the mize-bin to run generate
mod generated;
static APPNAME: &str = "mize";
macro_rules! compile_in_parts {
(
$mize:ident,
$(
$name:literal : $part:path
),+ $(,)?
) => {
let enable = $mize.get_config("enable")?;
let mut enable_str = Vec::new();
for name in enable.cbor().as_array().ok_or(mize_err!("config(enable) not a list"))? {
enable_str.push(name.as_text().ok_or("part name not a str")?);
}
$(
if enable_str.contains(&$name) {
tracing::info!("enabling compiled_in_part: {}", $name);
if let Err(e) = $part(&mut $mize) {
print!("part '{}' failed with: ", $name);
e.log();
}
}
)+
};
}
fn main() {
if let Err(e) = tried_main() {
e.log();
}
}
fn tried_main() -> MizeResult<()> {
let cli_matches = cli_matches();
// logging
init_logger(Some(&cli_matches));
// init mize
let mut mize = Mize::with_config(config_from_cli_args(&cli_matches)?)?;
// compiled in parts (which for now is all)
/*
compile_in_parts! {
mize,
"cli": marts::cli,
"auth": marts::part_client_auth,
"spacetime": marts::spacetime,
//"matrix": marts::part_matrix,
"ppc.server": marts::ppc::server::part_ppc_server,
"c2vi": marts::part_c2vi,
"sci.timetrack": marts::sci::timetrack::part_timetrack,
};
*/
// match subcommand
match cli_matches.subcommand() {
Some(("run", sub_matches)) => cli::run(sub_matches, &mut mize),
Some(("status", sub_matches)) => cli::status(sub_matches, &mut mize),
Some(("info", sub_matches)) => cli::info(sub_matches, &mut mize),
Some(("get", sub_matches)) => cli::get(sub_matches, &mut mize),
Some(("set", sub_matches)) => cli::set(sub_matches, &mut mize),
Some(("show", sub_matches)) => cli::show(sub_matches, &mut mize),
Some(("call", sub_matches)) => cli::call(sub_matches, &mut mize),
Some(("create", sub_matches)) => cli::create(sub_matches, &mut mize),
Some(("format-cbor", sub_matches)) => cli::format_cbor(sub_matches, &mut mize),
Some((cmd, sub_matches)) => cli::run_from_part(cmd, sub_matches, &mut mize),
None => Err(MizeError::new()
.msg("No subcommand was passed. use --help to list availavle comamnds.")),
}?;
Ok(())
}
fn cli_matches() -> clap::ArgMatches {
let main = Command::new(APPNAME)
.version(crate_version!())
.author("Sebastian Moser")
.about("MiZe")
.allow_external_subcommands(true)
.arg(
Arg::new("verbose")
.long("verbose")
.short('v')
.action(ArgAction::Count)
.global(true),
)
.arg(
Arg::new("log-level")
.long("log-level")
.value_name("LOGLEVEL")
.help("set the log-level to one of OFF, ERROR, WARN, INFO, DEBUG, TRACE")
.global(true),
)
.arg(
Arg::new("log")
.long("log")
.help("log file to use")
.global(true),
)
.arg(
Arg::new("config")
.long("config")
.help("config file path")
.global(true),
)
.arg(
Arg::new("secrets")
.long("secrets")
.help("file path to store secrets at")
.global(true),
)
.arg(
Arg::new("opt")
.short('o')
.long("opt")
.help("overwrite a config option")
.global(true),
)
.subcommand(
Command::new("get")
.aliases(["g"])
.arg(Arg::new("id").help("The id or path to get"))
.arg(Arg::new("recurse").short('r').action(ArgAction::Count))
.arg(
Arg::new("raw")
.long("raw")
.action(ArgAction::SetTrue)
.help("print the config opt value as a raw string"),
),
)
.subcommand(
Command::new("set")
.aliases(["s"])
.arg(Arg::new("id").help("The id or path to set"))
.arg(Arg::new("value").help("The value to set the path to")),
)
.subcommand(
Command::new("show")
.aliases(["so"])
.arg(Arg::new("id").help("The id or path to sub to and show")),
)
.subcommand(Command::new("call").aliases(["c"]))
.subcommand(Command::new("create").aliases(["cr"]))
.subcommand(Command::new("status").aliases(["st"]))
.subcommand(Command::new("run").allow_external_subcommands(true))
.subcommand(Command::new("format-cbor").aliases(["fc"]))
.arg_required_else_help(true);
return main.get_matches();
}

59
core/Cargo.toml Normal file
View File

@@ -0,0 +1,59 @@
[package]
name = "mize"
version = "0.0.1"
edition = "2024"
license = "GPL-3.0"
description = 'A strongly typed "filesystem" for the age of connectivity.'
repository = "https://git.ppc.social/ppc/mize"
[lib]
name = "mize"
path = "src/lib.rs"
crate-type = [ "cdylib", "rlib" ]
[features]
default = [ "target-os" ]
target-os = [ "clap", "home", "nix", "sysinfo", "ciborium/default", "tracing-subscriber", "async", "tracing-subscriber/env-filter", "libloading", "ciborium-io", "tracing-core", "tar", "flate2", "http_req", "tokio/net", "tokio", "tokio/rt-multi-thread", "tokio/io-util", "cmd_lib" ]
target-js = [ "wasm-bindgen", "console_error_panic_hook", "web-sys", "web-sys/Worker", "web-sys/Window", "web-sys/Request", "web-sys/RequestInit", "web-sys/RequestMode", "web-sys/Response", "web-sys/WorkerOptions", "web-sys/WorkerType", "serde-wasm-bindgen", "wasm-bindgen-futures", "wee_alloc", "ciborium/default" ]
async = [ "tokio", ]
[dependencies]
# core dependencies
colored = "2.0.0"
uuid = "1.3.0"
ciborium = { version = "0.2.2", default-features = false }
serde = "1.0.203"
interner = { git = "https://github.com/c2vi/interner" }
tracing = "0.1.40"
serde_json = { version = "1.0.117", optional = false }
toml = { version = "0.8.13", features = ["parse"], optional = false }
dyn-clone = "1.0.17"
sha2 = "0.10.8"
flume = { version = "0.11.1", default-features = false, features = [ "async" ] }
mize_macros = { path = "../macros" }
async-trait = "0.1.89"
# wasm dependencies
wasm-bindgen = { version = "0.2.84", optional = true }
console_error_panic_hook = { version = "0.1.7", optional = true }
web-sys = { version = "0.3.70", optional = true }
serde-wasm-bindgen = { version = "0.6.5", optional = true }
wasm-bindgen-futures = { version = "0.4.49", optional = true }
wee_alloc = { version = "0.4.5", optional = true }
# os dependencies
clap = { version = "4.4.11", features = ["cargo"], optional = true }
home = { version = "0.5.9", optional = true }
daemonize = { version = "0.5.0", optional = true }
nix = { version = "0.27.1", features = ["signal"], optional = true }
sysinfo = { version = "0.30.12", optional = true }
tracing-subscriber = { version = "0.3.18", optional = true }
tokio = { version = "1.46.1", features = ["sync", "rt" ], optional = true, default-features = false }
libloading = { version = "0.8.5", optional = true }
ciborium-io = { version = "0.2.2", optional = true }
tracing-core = { version = "0.1.32", optional = true }
tar = { version = "0.4.42", optional = true }
flate2 = { version = "1.0.33", optional = true }
http_req = { version = "0.12.0", default-features = false, features = [ "rust-tls" ], optional = true }
cmd_lib = { version = "2.0.1", optional = true }

11
core/src/api.rs Normal file
View File

@@ -0,0 +1,11 @@
use crate::prelude::*;
pub fn dyn_call(part_id: MizeId, method: &str, args: ItemData) -> MizeResult<ItemData> {
let mut mize = get_global_mize()?;
mize.dyn_call(part_id, method, args)
}
pub fn get_config(name: &str) -> MizeResult<ItemData> {
let mut mize = get_global_mize()?;
mize.get_config(name)
}

255
core/src/c_api.rs Normal file
View File

@@ -0,0 +1,255 @@
//! C ABI for dynamically loaded parts (see `include/mize.h`).
//!
//! Names passed to `opt` / `method` / `thread` / `get_config` / `set_config`
//! are absolute dotted paths (e.g. `"vicinae.open"`).
//!
//! Errors use a thread-local slot (like errno): C code calls `set_err(...)`;
//! Rust checks via [`take_err`] after the call.
use std::cell::RefCell;
use std::ffi::{c_char, c_void, CStr};
use std::ptr;
use crate::config::{ConfigOpt, MizeFun};
use crate::error::MizeError;
use crate::get_global_mize;
use crate::item::ItemData;
use crate::mize_err;
use crate::write_to_mize_stdout;
type CFun = unsafe extern "C" fn(*mut ItemData) -> *mut ItemData;
thread_local! {
/// Per-thread error set by C via `set_err`, consumed by Rust after the call.
static CUR_ERR: RefCell<Option<MizeError>> = const { RefCell::new(None) };
}
/// Clear the thread-local C error (call before invoking a C part entry / callback).
pub fn clear_err() {
CUR_ERR.with(|slot| {
*slot.borrow_mut() = None;
});
}
/// Take the thread-local C error, if any.
pub fn take_err() -> Option<MizeError> {
CUR_ERR.with(|slot| slot.borrow_mut().take())
}
/// Opaque pointer stored via `data_from_native` / read via `data_as_native`.
struct NativePtr(*mut c_void);
// SAFETY: the C++ part is responsible for only using the pointer on the right thread.
unsafe impl Send for NativePtr {}
unsafe impl Sync for NativePtr {}
fn cstr<'a>(p: *const c_char) -> Option<&'a str> {
if p.is_null() {
return None;
}
unsafe { CStr::from_ptr(p) }.to_str().ok()
}
fn wrap_c_fun(c_fun: CFun) -> MizeFun {
Box::new(move |args| {
clear_err();
let mut args_box = Box::new(args);
let ret_ptr = unsafe { c_fun(args_box.as_mut()) };
drop(args_box);
if let Some(err) = take_err() {
if !ret_ptr.is_null() {
unsafe { drop(Box::from_raw(ret_ptr)) };
}
return Err(err);
}
if ret_ptr.is_null() {
return Ok(ItemData::new());
}
Ok(*unsafe { Box::from_raw(ret_ptr) })
})
}
fn register_fun(name: &str, kind: &str, signature: &str, fun: MizeFun) {
let mut mize = match get_global_mize() {
Ok(m) => m,
Err(e) => {
let _ = write_to_mize_stdout(format_args!("[c_api] get_global_mize failed: {e:?}\n"));
return;
}
};
let id = match mize.id_new_dots(name) {
Ok(id) => id,
Err(e) => {
let _ = write_to_mize_stdout(format_args!("[c_api] bad id '{name}': {e:?}\n"));
return;
}
};
if mize.is_generate_mode().unwrap_or(false) {
let short = name.rsplit('.').next().unwrap_or(name);
let _ = mize.record_method(short, id.clone(), kind, signature);
return;
}
let opt = ConfigOpt {
name: id.dot(),
val: None,
fun: None,
deps: Vec::new(),
};
mize.config_opts.lock().unwrap().insert(id.clone(), opt);
mize.fun_values.lock().unwrap().insert(id, fun);
}
#[unsafe(no_mangle)]
pub extern "C" fn opt(name: *const c_char) {
let Some(name) = cstr(name) else { return };
let mut mize = match get_global_mize() {
Ok(m) => m,
Err(_) => return,
};
let Ok(id) = mize.id_new_dots(name) else { return };
if mize.is_generate_mode().unwrap_or(false) {
let short = name.rsplit('.').next().unwrap_or(name);
let _ = mize.record_opt(short, id.clone(), None);
return;
}
let opt = ConfigOpt {
name: id.dot(),
val: None,
fun: None,
deps: Vec::new(),
};
mize.config_opts.lock().unwrap().insert(id, opt);
}
#[unsafe(no_mangle)]
pub extern "C" fn method(name: *const c_char, signature: *const c_char, c_fun: CFun) {
let Some(name) = cstr(name) else { return };
let sig = cstr(signature).unwrap_or("");
register_fun(name, "method", sig, wrap_c_fun(c_fun));
}
#[unsafe(no_mangle)]
pub extern "C" fn effect(name: *const c_char, c_fun: CFun) {
let Some(name) = cstr(name) else { return };
register_fun(name, "effect", "", wrap_c_fun(c_fun));
}
#[unsafe(no_mangle)]
pub extern "C" fn apply(name: *const c_char, c_fun: CFun) {
let Some(name) = cstr(name) else { return };
register_fun(name, "apply", "", wrap_c_fun(c_fun));
}
#[unsafe(no_mangle)]
pub extern "C" fn thread(name: *const c_char, c_fun: CFun) {
let Some(name) = cstr(name) else { return };
register_fun(name, "thread", "", wrap_c_fun(c_fun));
}
#[unsafe(no_mangle)]
pub extern "C" fn set_config(name: *const c_char, value: *mut ItemData) {
if value.is_null() {
return;
}
let Some(name) = cstr(name) else {
unsafe { drop(Box::from_raw(value)) };
return;
};
let value = unsafe { *Box::from_raw(value) };
let mut mize = match get_global_mize() {
Ok(m) => m,
Err(_) => return,
};
let Ok(id) = mize.id_new_dots(name) else { return };
let mut opts = mize.config_opts.lock().unwrap();
match opts.get_mut(&id) {
Some(opt) => {
opt.val = Some(value);
}
None => {
opts.insert(
id.clone(),
ConfigOpt {
name: id.dot(),
val: Some(value),
fun: None,
deps: Vec::new(),
},
);
}
}
}
#[unsafe(no_mangle)]
pub extern "C" fn get_config(name: *const c_char) -> *mut ItemData {
let Some(name) = cstr(name) else {
return ptr::null_mut();
};
let mut mize = match get_global_mize() {
Ok(m) => m,
Err(_) => return ptr::null_mut(),
};
match mize.get_config(name) {
Ok(data) => Box::into_raw(Box::new(data)),
Err(_) => ptr::null_mut(),
}
}
#[unsafe(no_mangle)]
pub extern "C" fn data_new() -> *mut ItemData {
Box::into_raw(Box::new(ItemData::new()))
}
#[unsafe(no_mangle)]
pub extern "C" fn data_from_native(ptr: *mut c_void) -> *mut ItemData {
match ItemData::from_native(NativePtr(ptr)) {
Ok(data) => Box::into_raw(Box::new(data)),
Err(e) => {
let _ = write_to_mize_stdout(format_args!("[c_api] data_from_native: {e:?}\n"));
ptr::null_mut()
}
}
}
#[unsafe(no_mangle)]
pub extern "C" fn data_as_native(data: *mut ItemData) -> *mut c_void {
if data.is_null() {
return ptr::null_mut();
}
let data = unsafe { &*data };
match data.native::<NativePtr>() {
Ok(guard) => guard.0,
Err(e) => {
let _ = write_to_mize_stdout(format_args!("[c_api] data_as_native: {e:?}\n"));
ptr::null_mut()
}
}
}
#[unsafe(no_mangle)]
pub extern "C" fn data_free(data: *mut ItemData) {
if !data.is_null() {
unsafe { drop(Box::from_raw(data)) };
}
}
#[unsafe(no_mangle)]
pub extern "C" fn mize_write_stdout(str_ptr: *const c_char) {
let Some(s) = cstr(str_ptr) else { return };
let _ = write_to_mize_stdout(format_args!("{s}"));
}
/// Set the thread-local error (like errno). `load_part` / C-callback wrappers turn this into `Err`.
#[unsafe(no_mangle)]
pub extern "C" fn set_err(msg: *const c_char) {
let text = cstr(msg).unwrap_or("set_err called with null/invalid message");
CUR_ERR.with(|slot| {
*slot.borrow_mut() = Some(mize_err!("{}", text));
});
}

287
core/src/config.rs Normal file
View File

@@ -0,0 +1,287 @@
use std::any::Any;
use std::fmt::Debug;
use crate::MizeResult;
use crate::get_global_mize;
use crate::id::MizeId;
use crate::item::{IntoItemData, ItemData};
use crate::Mize;
pub type ConfigFun = Box<dyn Fn() -> MizeResult<ItemData> + Send + Sync + 'static>;
pub type MizeFun = Box<dyn Fn(ItemData) -> MizeResult<ItemData> + Send + Sync + 'static>;
pub struct ConfigOpt {
pub name: String,
pub val: Option<ItemData>,
pub fun: Option<ConfigFun>,
pub deps: Vec<String>,
}
pub trait FromItemData {
fn from_itemdata(data: ItemData) -> MizeResult<Self> where Self: Sized;
}
impl Debug for ConfigOpt {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "({}: {})", self.name, self.val.clone().unwrap_or(ItemData::new()))
}
}
#[derive(Clone)]
#[repr(C)]
pub struct ConfigOptId {
pub id: MizeId,
}
impl ConfigOptId {
pub fn default_val(self, val: ItemData) -> Self {
let mize = get_global_mize().unwrap();
// in generate mode: record the default value as metadata, skip normal op
if mize.is_generate_mode().unwrap_or(false) {
mize.record_opt("_default_val", self.id.clone(), Some(val)).ok();
return self.clone();
}
let mut config_opts = mize.config_opts.lock().unwrap();
let opt = config_opts.get_mut(&self.id).unwrap();
opt.val = Some(val);
self.clone()
}
pub fn fun(self, fun: ConfigFun) -> Self {
let mize = get_global_mize().unwrap();
// in generate mode: record as an opt with a fun, skip normal op
if mize.is_generate_mode().unwrap_or(false) {
mize.record_opt("_fun", self.id.clone(), None).ok();
return self.clone();
}
let mut config_opts = mize.config_opts.lock().unwrap();
let opt = config_opts.get_mut(&self.id).unwrap();
opt.fun = Some(fun);
self.clone()
}
pub fn method<F: Fn(ItemData) -> MizeResult<ItemData> + Send + Sync + 'static>(self, name: &str, signature: &str, fun: F) -> ConfigOptId {
let mize = get_global_mize().unwrap();
let mut cur_part = self.id.clone();
let id = cur_part.join(name).unwrap();
// in generate mode: record method metadata, skip normal registration
if mize.is_generate_mode().unwrap_or(false) {
mize.record_method(name, id.clone(), "method", signature).ok();
return self.clone();
}
let mut opts = mize.config_opts.lock().unwrap();
let opt = ConfigOpt {
name: id.dot(),
val: None,
fun: None,
deps: Vec::new(),
};
let mut fun_values = mize.fun_values.lock().unwrap();
fun_values.insert(id.clone(), Box::new(fun));
opts.insert(id.clone(), opt);
self.clone()
}
}
//pub trait ToItemDataHelper {
//fn into_item_data(self) -> ItemData;
//}
pub trait MizeAny {
fn as_raw_ptr(&self) -> *const ();
}
impl<T> MizeAny for T {
fn as_raw_ptr(&self) -> *const () {
self as *const Self as *const ()
}
}
/// Load the `.so` for `part_name`, but register it under `at_id`
/// (e.g. `load_part_at("priv.spacetime", "spacetime")`).
/// No-op in generate mode — bindings come from listing the part in `gen = [...]`.
pub fn load_part_at(at_id: &str, part_name: &str) -> MizeResult<()> {
let mize = get_global_mize()?;
if mize.is_generate_mode().unwrap_or(false) {
return Ok(());
}
crate::platform::load_part(part_name, None, Some(at_id))
}
pub fn opt(name: &str) -> crate::config::ConfigOptId {
let mize = get_global_mize().unwrap();
let mut cur_part = mize.cur_eval_part.lock().unwrap().clone().unwrap();
let id = cur_part.join(name).unwrap();
// in generate mode: record opt metadata, skip normal registration
if mize.is_generate_mode().unwrap_or(false) {
mize.record_opt(name, id.clone(), None).ok();
return ConfigOptId { id };
}
let mut opts = mize.config_opts.lock().unwrap();
let opt = ConfigOpt {
name: id.dot(),
val: None,
fun: None,
deps: Vec::new(),
};
opts.insert(id.clone(), opt);
ConfigOptId { id }
}
pub fn method<F: Fn(ItemData) -> MizeResult<ItemData> + Send + Sync + 'static>(name: &str, signature: &str, fun: F) -> ConfigOptId {
let mize = get_global_mize().unwrap();
let mut cur_part = mize.cur_eval_part.lock().unwrap().clone().unwrap();
let id = cur_part.join(name).unwrap();
// in generate mode: record method metadata, skip normal registration
if mize.is_generate_mode().unwrap_or(false) {
mize.record_method(name, id.clone(), "method", signature).ok();
return ConfigOptId { id };
}
let mut opts = mize.config_opts.lock().unwrap();
let opt = ConfigOpt {
name: id.dot(),
val: None,
fun: None,
deps: Vec::new(),
};
let mut fun_values = mize.fun_values.lock().unwrap();
fun_values.insert(id.clone(), Box::new(fun));
opts.insert(id.clone(), opt);
ConfigOptId { id }
}
pub fn effect<F: Fn(ItemData) -> MizeResult<ItemData> + Send + Sync + 'static>(name: &str, fun: F) -> ConfigOptId {
let mize = get_global_mize().unwrap();
let mut cur_part = mize.cur_eval_part.lock().unwrap().clone().unwrap();
let id = cur_part.join(name).unwrap();
// in generate mode: record effect metadata, skip normal registration
if mize.is_generate_mode().unwrap_or(false) {
mize.record_method(name, id.clone(), "effect", "").ok();
return ConfigOptId { id };
}
let mut opts = mize.config_opts.lock().unwrap();
let opt = ConfigOpt {
name: id.dot(),
val: None,
fun: None,
deps: Vec::new(),
};
let mut fun_values = mize.fun_values.lock().unwrap();
fun_values.insert(id.clone(), Box::new(fun));
opts.insert(id.clone(), opt);
ConfigOptId { id }
}
pub fn apply<F: Fn(ItemData) -> MizeResult<ItemData> + Send + Sync + 'static>(name: &str, fun: F) -> ConfigOptId {
let mize = get_global_mize().unwrap();
let mut cur_part = mize.cur_eval_part.lock().unwrap().clone().unwrap();
let id = cur_part.join(name).unwrap();
// in generate mode: record apply metadata, skip normal registration
if mize.is_generate_mode().unwrap_or(false) {
mize.record_method(name, id.clone(), "apply", "").ok();
return ConfigOptId { id };
}
let mut opts = mize.config_opts.lock().unwrap();
let opt = ConfigOpt {
name: id.dot(),
val: None,
fun: None,
deps: Vec::new(),
};
let mut fun_values = mize.fun_values.lock().unwrap();
fun_values.insert(id.clone(), Box::new(fun));
opts.insert(id.clone(), opt);
ConfigOptId { id }
}
pub fn thread<F: Fn(ItemData) -> MizeResult<ItemData> + Send + Sync + 'static>(name: &str, fun: F) -> ConfigOptId {
let mize = get_global_mize().unwrap();
let mut cur_part = mize.cur_eval_part.lock().unwrap().clone().unwrap();
let id = cur_part.join(name).unwrap();
// in generate mode: record thread metadata, skip normal registration
if mize.is_generate_mode().unwrap_or(false) {
mize.record_method(name, id.clone(), "thread", "").ok();
return ConfigOptId { id };
}
let mut opts = mize.config_opts.lock().unwrap();
let opt = ConfigOpt {
name: id.dot(),
val: None,
fun: None,
deps: Vec::new(),
};
let mut fun_values = mize.fun_values.lock().unwrap();
fun_values.insert(id.clone(), Box::new(fun));
opts.insert(id.clone(), opt);
ConfigOptId { id }
}
type CFun = unsafe extern "C" fn (*mut ItemData) -> *mut ItemData;
pub mod c_api {
use std::ffi::c_char;
use crate::config::{CFun, ConfigOptId};
use std::ffi::CStr;
pub extern "C" fn opt(name: *const c_char) -> ConfigOptId {
unsafe {
let c_str = CStr::from_ptr(name);
let string = c_str.to_string_lossy();
super::opt(&string)
}
}
pub extern "C" fn method(name: *const c_char, signature: *const c_char, c_fun: CFun) -> ConfigOptId {
unsafe {
let name_str = CStr::from_ptr(name).to_string_lossy();
let sig_str = if signature.is_null() {
String::new()
} else {
CStr::from_ptr(signature).to_string_lossy().into_owned()
};
super::method(&name_str, &sig_str, move |args| {
let raw_ptr = Box::into_raw(Box::new(args));
let ret_ptr = c_fun(raw_ptr);
let data = *Box::from_raw(ret_ptr);
Ok(data)
})
}
}
pub extern "C" fn effect(name: *const c_char, c_fun: CFun) -> ConfigOptId {
unsafe {
let c_str = CStr::from_ptr(name);
let string = c_str.to_string_lossy();
super::effect(&string, move |args| {
let raw_ptr = Box::into_raw(Box::new(args));
let ret_ptr = c_fun(raw_ptr);
let data = *Box::from_raw(ret_ptr);
Ok(data)
})
}
}
pub extern "C" fn apply(name: *const c_char, c_fun: CFun) -> ConfigOptId {
unsafe {
let c_str = CStr::from_ptr(name);
let string = c_str.to_string_lossy();
super::apply(&string, move |args| {
let raw_ptr = Box::into_raw(Box::new(args));
let ret_ptr = c_fun(raw_ptr);
let data = *Box::from_raw(ret_ptr);
Ok(data)
})
}
}
pub extern "C" fn thread(name: *const c_char, c_fun: CFun) -> ConfigOptId {
unsafe {
let c_str = CStr::from_ptr(name);
let string = c_str.to_string_lossy();
super::thread(&string, move |args| {
let raw_ptr = Box::into_raw(Box::new(args));
let ret_ptr = c_fun(raw_ptr);
let data = *Box::from_raw(ret_ptr);
Ok(data)
})
}
}
}

View File

@@ -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;
@@ -7,13 +9,13 @@ use std::fmt::Debug;
use std::fmt::Display; use std::fmt::Display;
use std::io; use std::io;
use std::string::FromUtf8Error; use std::string::FromUtf8Error;
use tracing::{debug, error, info, trace, warn}; use crate::prelude::*;
//use crate::proto::MizeMessage; //use crate::proto::MizeMessage;
#[macro_export] #[macro_export]
macro_rules! mize_err { macro_rules! mize_err {
($($arg:tt)*) => { MizeError::new().msg(format!( $($arg)*)) }; ($($arg:tt)*) => { $crate::error::MizeError::new().msg(format!( $($arg)*)) };
} }
pub type MizeResult<T> = Result<T, MizeError>; pub type MizeResult<T> = Result<T, MizeError>;
@@ -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 envountered!"); 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)),
}
}
}

View File

@@ -1,17 +1,11 @@
use core::fmt; use core::fmt;
use interner::{ use interner::{
shared::{SharedPool, SharedString, SharedVecString}, shared::{SharedString, SharedVecString},
Pooled,
}; };
use std::collections::hash_map::RandomState; use std::hash::Hash;
use std::path::Path;
use std::{collections::binary_heap::Iter, fmt::write, hash::Hash};
use crate::error::{MizeError, MizeResult, MizeResultTrait}; use crate::{get_global_mize, error::MizeResult};
use crate::{ use crate::{instance::Mize, mize_err};
instance::{store::Store, Mize},
mize_err,
};
#[derive(Debug, Clone, Hash, Eq, PartialEq)] #[derive(Debug, Clone, Hash, Eq, PartialEq)]
pub struct MizeId { pub struct MizeId {
@@ -38,6 +32,24 @@ pub trait IntoMizeId {
} }
impl MizeId { impl MizeId {
pub fn dot(&self) -> String {
let path = self.path();
let mut new_path = Vec::new();
for el in path.iter() {
new_path.push(el.to_owned());
}
new_path.join(".")
}
pub fn join(&mut self, to_add: &str) -> MizeResult<MizeId> {
let path = self.path();
let mut new_path = Vec::new();
for el in path.iter() {
new_path.push(el.to_owned());
}
new_path.push(to_add.to_owned());
let mize = get_global_mize()?;
Ok(new_path.to_mize_id(&mize)?)
}
pub fn store_part(&self) -> &str { pub fn store_part(&self) -> &str {
self.path self.path
.iter() .iter()
@@ -59,7 +71,7 @@ impl MizeId {
pub fn after_store_part(&self) -> Vec<String> { pub fn after_store_part(&self) -> Vec<String> {
let tmp = self.path(); let tmp = self.path();
let mut path_iter = tmp.into_iter(); let mut path_iter = <Box<[std::string::String]> as Clone>::clone(&tmp).into_iter();
path_iter.next(); path_iter.next();
let new_path: Vec<String> = path_iter.map(|v| v.to_owned()).collect(); let new_path: Vec<String> = path_iter.map(|v| v.to_owned()).collect();
@@ -130,7 +142,7 @@ impl IntoMizeId for Vec<&str> {
} }
} }
impl IntoMizeId for MizeId { impl IntoMizeId for MizeId {
fn to_mize_id(self, instance: &Mize) -> MizeResult<MizeId> { fn to_mize_id(self, _instance: &Mize) -> MizeResult<MizeId> {
Ok(self) Ok(self)
} }
} }

61
core/src/include/mize.h Normal file
View File

@@ -0,0 +1,61 @@
#pragma once
/**
* Mize C API — thin header over libmize.so exports (core/src/c_api.rs).
*
* Names for opt / method / thread / get_config / set_config are absolute
* dotted paths for now (e.g. "vicinae.open", "vicinae.nav").
*
* Errors: thread-local, like errno. Call set_err("...") from C; Rust
* (load_part / method wrappers) turns that into Err. Part entry is
* `void part_<name>(void)` — no sret / Result return.
*
* ItemData (`DATA`) is opaque. Ownership:
* - Callbacks receive a borrowed `args` (do not free).
* - Callbacks must return an owned DATA* (e.g. data_new()); may return NULL for empty.
* - get_config returns owned DATA*; free with data_free.
* - set_config takes ownership of the value pointer.
*/
#include <stddef.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef struct ItemData DATA;
/** C method / thread / effect callback. args is borrowed; return owned DATA* or NULL. */
typedef DATA *(*CFun)(DATA *args);
void opt(const char *name);
void method(const char *name, const char *signature, CFun c_fun);
void effect(const char *name, CFun c_fun);
void thread(const char *name, CFun c_fun);
void set_config(const char *name, DATA *value);
DATA *get_config(const char *name);
DATA *data_new(void);
/** Store an opaque pointer in the native-value registry. */
DATA *data_from_native(void *ptr);
/** Read back a pointer previously stored with data_from_native. Returns NULL on error. */
void *data_as_native(DATA *data);
void data_free(DATA *data);
void mize_write_stdout(const char *str);
/** Set the thread-local error message (consumed by Rust after the call). */
void set_err(const char *msg);
#ifdef __cplusplus
}
#endif

View File

@@ -41,9 +41,9 @@ pub fn value_raw_from_peer(item: &mut Item) -> MizeResult<ItemData> {
let conn_id_str = id.nth_part(2)?; let conn_id_str = id.nth_part(2)?;
let conn_id: u64 = conn_id_str.parse()?; let conn_id: u64 = conn_id_str.parse()?;
let tmp_path = id.path(); let tmp_path = id.path();
let mut new_id_str = tmp_path.into_iter(); let mut new_id_str = tmp_path.iter();
let skipped = new_id_str.skip(4); let skipped = new_id_str.skip(4);
let new_id = item.instance.new_id(skipped.collect::<Vec<&String>>())?; let new_id = item.instance.new_id(skipped.cloned().collect::<Vec<String>>())?;
let connection = item.instance.get_connection(conn_id)?; let connection = item.instance.get_connection(conn_id)?;

1088
core/src/instance/mod.rs Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,2 @@
// Message thread handling for the Mize instance.
// This module is reserved for future message threading functionality.

View File

@@ -2,13 +2,13 @@ use core::fmt;
use flume::Sender; use flume::Sender;
use std::rc::Rc; use std::rc::Rc;
use std::sync::Arc; use std::sync::Arc;
use tracing::trace;
use crate::error::MizeResult; use crate::error::MizeResult;
use crate::id::MizeId; use crate::id::MizeId;
use crate::instance::connection::Connection; use crate::instance::connection::Connection;
use crate::item::Item; use crate::item::Item;
use crate::proto::MizeMessage; use crate::proto::MizeMessage;
use crate::prelude::*;
use super::Mize; use super::Mize;

View File

@@ -1,5 +1,3 @@
use tracing_subscriber::registry::Data;
use crate::item::IntoItemData; use crate::item::IntoItemData;
use super::*; use super::*;
@@ -43,21 +41,21 @@ fn test_sort_keys() -> MizeResult<()> {
fn test_get_module_hash() -> MizeResult<()> { fn test_get_module_hash() -> MizeResult<()> {
let mut instance = Mize::empty()?; let mut instance = Mize::empty()?;
use crate::platform::os::get_module_hash; use crate::platform::os::get_part_hash;
let selector_toml = r#" let selector_toml = r#"
mize_version = "0.0.0" mize_version = "0.0.0"
system = "x86_64-unknown-linux-gnu" system = "x86_64-unknown-linux-gnu"
toolchain_version = "rustc 1.78.0 (9b00956e5 2024-04-29)" toolchain_version = "rustc 1.78.0 (9b00956e5 2024-04-29)"
"#; "#;
instance.set_blocking("0/config/selector", ItemData::from_toml(&selector_toml)?)?; // Pass selector fields as hash options (merges over defaults).
let options = ItemData::from_toml(&selector_toml)?;
let (hash, selector) = get_part_hash(&mut instance, "String", options)?;
let (hash, selector) = get_module_hash(&mut instance, "String", ItemData::new())?; println!("hash: {}", hash);
println!("selector: {}", selector);
test_println!("hash: {}", hash); assert_eq!(hash, "227b0216f9b7a324a621ff773c5241c9".to_owned());
test_println!("selector: {}", selector);
assert_eq!(hash, "e96edf60216688314efc263c1aa8d6ce".to_owned());
Ok(()) Ok(())
} }
@@ -70,7 +68,7 @@ fn test_set_store_path() -> MizeResult<()> {
let config = instance.get("0/config")?.as_data_full()?; let config = instance.get("0/config")?.as_data_full()?;
test_println!("data at 0/config: {}", config); println!("data at 0/config: {}", config);
instance.set_blocking("0/config/store_path", store_path.clone().into_item_data())?; instance.set_blocking("0/config/store_path", store_path.clone().into_item_data())?;
@@ -252,12 +250,3 @@ fn test_item_merge() -> MizeResult<()> {
Ok(()) Ok(())
} }
/*
#[test]
#[should_panic(expected = "correct panic")]
fn test_cant_set_non_existent_item() -> () {
let instance = Instance::empty().expect("wrong panic");
instance.set_blocking("5", "hello world".into_item_data()).expect("correct panic");
}
*/

View File

@@ -1,11 +1,11 @@
use flume::Receiver; use flume::Receiver;
use std::borrow::BorrowMut; use std::borrow::BorrowMut;
use std::sync::Arc; use std::sync::Arc;
use tracing::{error, trace, warn};
use crate::error::{MizeError, MizeResult, MizeResultTrait}; use crate::error::{MizeError, MizeResult, MizeResultTrait};
use crate::id::MizeId; use crate::id::MizeId;
use crate::mize_err; use crate::mize_err;
use crate::prelude::*;
use crate::proto::{MessageCmd, MizeMessage}; use crate::proto::{MessageCmd, MizeMessage};
use crate::{instance::Mize, item::ItemData}; use crate::{instance::Mize, item::ItemData};
@@ -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"));
} }

1686
core/src/item.rs Normal file

File diff suppressed because it is too large Load Diff

308
core/src/lib.rs Normal file
View File

@@ -0,0 +1,308 @@
#![allow(warnings)]
#![feature(str_as_str)]
use std::cell::RefCell;
use std::fmt::format;
#[macro_export]
macro_rules! out {
($($arg:tt)*) => {
// format_args! handles string interpolation safely at compile time
if let Err(e) = $crate::write_to_mize_stdout(format_args!("{}\n", format_args!($($arg)*))) {
// Choose your error handling behavior here (panic, print to stderr, or ignore)
eprintln!("out! macro failed to write!!");
}
};
}
pub fn write_to_mize_stdout(args: std::fmt::Arguments) -> MizeResult<()> {
print!("{}", args);
Ok(())
}
/// Mize Shell (msh) macro - wraps command execution with nixpkgs executable resolution.
///
/// If the executable is given as a bare name (identifier), it is resolved via
/// [`msh_get_nix_bin`] to a nixpkgs store path. If it is given as a string literal
/// containing `/`, it is treated as a path and used as-is.
///
/// # Examples
/// ```ignore
/// msh!(git status)?; // resolves `git` from nixpkgs
/// msh!(curl https://example.com)?;
/// msh!("/usr/bin/git" status)?; // uses path directly
/// ```
#[macro_export]
macro_rules! msh {
// First token is a string literal — check if it's a path at runtime
($cmd:literal $($rest:tt)*) => {{
let cmd_str: &str = $cmd;
let resolved: String = if cmd_str.contains('/') {
cmd_str.to_string()
} else {
$crate::msh_get_nix_bin(cmd_str)
};
let mut cmd = ::std::process::Command::new(resolved);
$(cmd.arg(::std::stringify!($rest));)*
let status = cmd.status()?;
if status.success() {
::std::result::Result::Ok(())
} else {
::std::result::Result::Err(::std::io::Error::new(
::std::io::ErrorKind::Other,
::std::format!("msh: command exited with {}", status),
))
}
}};
// First token is an identifier — always resolve via nixpkgs
($cmd:ident $($rest:tt)*) => {{
let resolved: String = $crate::msh_get_nix_bin(::std::stringify!($cmd));
let mut cmd = ::std::process::Command::new(resolved);
$(cmd.arg(::std::stringify!($rest));)*
let status = cmd.status()?;
if status.success() {
::std::result::Result::Ok(())
} else {
::std::result::Result::Err(::std::io::Error::new(
::std::io::ErrorKind::Other,
::std::format!("msh: command exited with {}", status),
))
}
}};
}
pub mod prelude {
pub use super::*;
pub use instance::Mize;
pub use id::*;
pub use error::MizeError;
pub use crate::mize_err;
pub use error::MizeResult;
pub use error::IntoMizeResult;
pub use item::{Item, ItemData, IntoItemData};
pub use config::*;
pub use api::*;
pub use mize_macros::*;
pub use crate::tracing::{trace, warn, debug, info, error};
}
pub use flume;
use tokio::runtime::Builder;
use tokio::runtime::Runtime;
pub use tracing;
// the core part has the code that can run on any platform
pub mod config;
pub mod error;
pub mod id;
pub mod instance;
pub mod item;
pub mod memstore;
pub mod proto;
pub mod types;
pub mod c_api;
pub mod api;
pub use async_trait::async_trait;
pub use ciborium;
pub use error::MizeError;
pub use error::MizeResult;
pub use instance::Mize;
pub use instance::{
MizePart, PartMetadata, OptMetadata, MethodMetadata, NativeMetadata, parse_method_signature,
};
pub use mize_macros::*;
use std::path::PathBuf;
use std::sync::{Arc, Mutex, OnceLock};
use crate::platform::os;
/// Global Mize instance storage.
/// This is set once when the Mize instance is created and can be accessed from anywhere.
pub static GLOBAL_MIZE: OnceLock<Arc<Mutex<Mize>>> = OnceLock::new();
/// Get the global Mize instance.
/// Returns a clone of the Mize instance (which is cheap as it only clones Arcs internally).
pub fn get_global_mize() -> MizeResult<Mize> {
let arc = GLOBAL_MIZE
.get()
.ok_or_else(|| mize_err!("No global Mize instance set"))?;
let mize = arc
.lock()
.map_err(|_| mize_err!("Failed to lock global Mize"))?;
Ok(mize.clone())
}
/// Set the global Mize instance.
pub fn set_global_mize(mize: Mize) -> MizeResult<()> {
let arc = Arc::new(Mutex::new(mize.clone()));
if let Err(_) = GLOBAL_MIZE.set(arc) {
let a = GLOBAL_MIZE.get().unwrap();
let mut b = a.lock().map_err(|_| mize_err!("Failed to lock global Mize"))?;
*b = mize;
}
Ok(())
}
/// C API function to set the global Mize instance from a dynamically loaded part.
/// This is a hack to allow Rust parts loaded as .so files to access the global Mize instance.
// i hate rust.
#[unsafe(no_mangle)]
pub extern "C" fn mize_rust_hack_set_global(mize: Arc<Mutex<Mize>>, dispatch_ptr: *const std::ffi::c_void) {
// and also set the tokio runtime
//let mize_locked = mize.lock().unwrap();
//let rt = mize_locked.runtime.lock().unwrap();
//rt.enter();
//drop(rt);
//drop(mize_locked);
let rt = Builder::new_multi_thread().enable_all().build().unwrap();
let guard = rt.enter();
std::mem::forget(guard);
use crate::tracing::Dispatch;
let _ = GLOBAL_MIZE.set(mize);
//and also init tracing
unsafe {
/// i hate rust.
let dispatch = &*(dispatch_ptr as *const Dispatch);
if let Err(e) = tracing::dispatcher::set_global_default(dispatch.clone()) {
println!("failed to set tracing dispatcher: {e}");
}
tracing::callsite::rebuild_interest_cache();
}
}
/// Resolve an executable name to its full path in the nixpkgs store.
///
/// Reads `nix.pkgs.ref` (nixpkgs commit hash) and `nix.bin` (path to the `nix`
/// binary) from the Mize config, then runs:
///
/// ```text
/// <nix.bin> eval --raw nixpkgs/<hash>#<pkg>
/// ```
///
/// and returns `<outPath>/bin/<executable>`.
///
/// Certain executable names are mapped to different nixpkgs attribute names
/// via a hardcoded match (e.g. `npm` → `nodejs`).
///
/// # Panics
/// Panics if the global Mize instance is not set, the config values are missing,
/// or `nix eval` fails.
pub fn msh_get_nix_bin(name: &str) -> String {
// Hardcoded mapping of executable names to nixpkgs attribute names
let pkg = match name {
"npm" => "nodejs",
"node" => "nodejs",
"yarn" => "yarn",
"pnpm" => "nodePackages.pnpm",
_ => name,
};
let mut mize = get_global_mize().expect("msh_get_nix_bin: no global Mize instance set");
let nixpkgs_ref = mize
.get_config("nix.pkgs.ref")
.expect("msh_get_nix_bin: failed to get config nix.pkgs.ref")
.value_string()
.expect("msh_get_nix_bin: nix.pkgs.ref is not a string");
let nix_bin = mize
.get_config("nix.bin")
.expect("msh_get_nix_bin: failed to get config nix.bin")
.value_string()
.expect("msh_get_nix_bin: nix.bin is not a string");
// Run: nix eval --raw nixpkgs/<hash>#<pkg>
// --raw outputs just the store path without quotes
let attr = format!("nixpkgs/{}#{}", nixpkgs_ref, pkg);
let output = std::process::Command::new(&nix_bin)
.args(["eval", "--raw", &attr])
.output()
.expect("msh_get_nix_bin: failed to spawn nix eval");
if !output.status.success() {
panic!(
"msh_get_nix_bin: nix eval failed for {}: {}",
attr,
String::from_utf8_lossy(&output.stderr)
);
}
let out_path = String::from_utf8(output.stdout)
.expect("msh_get_nix_bin: nix eval output is not valid UTF-8")
.trim()
.to_string();
format!("{}/bin/{}", out_path, name)
}
/// Run a command with Mize Shell executable resolution.
///
/// This is the function-based equivalent of the [`msh!`] macro, with proper
/// argument handling (no `stringify!` limitations).
///
/// If `cmd` contains `/` it is treated as a path and used as-is.
/// Otherwise it is resolved via [`msh_get_nix_bin`].
///
/// Returns `Ok(())` if the command exits successfully, or an `io::Error` otherwise.
pub fn msh_run(cmd: &str, args: &[&str]) -> std::io::Result<()> {
let resolved = if cmd.contains('/') {
cmd.to_string()
} else {
msh_get_nix_bin(cmd)
};
let mut command = std::process::Command::new(resolved);
command.args(args);
let status = command.status()?;
if status.success() {
Ok(())
} else {
Err(std::io::Error::new(
std::io::ErrorKind::Other,
format!("msh: command exited with {}", status),
))
}
}
// platform specific stuff
pub mod platform {
use crate::{Mize, MizeResult};
#[cfg(feature = "target-js")]
pub mod js;
#[cfg(feature = "target-os")]
pub mod os;
pub fn init(mize: &mut Mize) -> MizeResult<()> {
#[cfg(feature = "target-os")] {
os::os_instance_init(mize)?;
}
#[cfg(feature = "target-js")] {
js::wasm_instance_init(mize)?;
}
Ok(())
}
pub fn load_part(part_name: &str, path: Option<String>, at_id: Option<&str>) -> MizeResult<()> {
#[cfg(feature = "target-os")] {
os::load_part(part_name, path, at_id)?;
}
Ok(())
}
pub fn fetch_part(mize: &mut Mize, part_name: &str) -> MizeResult<String> {
#[cfg(feature = "target-os")] {
os::fetch_part(mize, part_name)?;
}
Ok("alsdkjf".to_owned())
}
}

View File

@@ -4,7 +4,7 @@ use std::collections::binary_heap::Iter;
use std::collections::HashMap; use std::collections::HashMap;
use std::iter::Map; use std::iter::Map;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use tracing::{instrument, trace}; use crate::tracing::instrument;
use crate::error::{IntoMizeResult, MizeError, MizeResult}; use crate::error::{IntoMizeResult, MizeError, MizeResult};
use crate::id::MizeId; use crate::id::MizeId;
@@ -13,6 +13,7 @@ use crate::instance::Mize;
use crate::item::get_raw_from_cbor; use crate::item::get_raw_from_cbor;
use crate::item::{Item, ItemData}; use crate::item::{Item, ItemData};
use crate::mize_err; use crate::mize_err;
use crate::prelude::*;
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct MemStore { pub struct MemStore {
@@ -145,7 +146,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
)) ))

View File

@@ -0,0 +1,8 @@
{
"tasks": {
"dev": "deno run --watch main.ts"
},
"imports": {
"@std/assert": "jsr:@std/assert@1"
}
}

View File

@@ -1,79 +1,126 @@
use core::convert::From;
use core::option::Option;
use std::ptr::NonNull;
use std::panic;
use web_sys::js_sys::{self, eval, Promise};
use crate::id::MizeId; use crate::id::MizeId;
use crate::item::{Item, ItemData}; use crate::item::{Item, ItemData};
use crate::platform::wasm::js_sys::Function; use crate::platform::js::js_sys::Function;
use web_sys::{WorkerOptions, WorkerType}; use core::convert::From;
use core::option::Option;
use std::panic;
use std::ptr::NonNull;
use web_sys::js_sys::{self, eval, Promise};
use web_sys::Worker; use web_sys::Worker;
use web_sys::{WorkerOptions, WorkerType};
use crate::instance::{self, Instance};
use crate::error::MizeResult;
use crate::{mize_err, Module};
use crate::MizeError;
use crate::core::item::IntoItemData; use crate::core::item::IntoItemData;
use crate::instance::module::EmptyModule; use crate::error::MizeResult;
use crate::Mize;
use crate::MizeError;
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
#[cfg(feature = "wasm-target")] #[cfg(feature = "target-js")]
use wasm_bindgen::prelude::*; use wasm_bindgen::prelude::*;
#[cfg(feature = "wasm-target")] #[cfg(feature = "target-js")]
#[wasm_bindgen] #[wasm_bindgen]
extern "C" { extern "C" {
#[wasm_bindgen(js_namespace = console)] #[wasm_bindgen(js_namespace = console)]
fn log(s: &str); fn log(s: &str);
} }
#[cfg(feature = "wasm-target")] #[cfg(feature = "target-js")]
macro_rules! console_log { macro_rules! console_log {
// Note that this is using the `log` function imported above during // Note that this is using the `log` function imported above during
// `bare_bones` // `bare_bones`
($($t:tt)*) => (unsafe { log(&format_args!($($t)*).to_string())}) ($($t:tt)*) => (unsafe { log(&format_args!($($t)*).to_string())})
} }
#[cfg(not(feature = "wasm-target"))] #[cfg(not(feature = "target-js"))]
macro_rules! console_log { macro_rules! console_log {
// Note that this is using the `log` function imported above during // Note that this is using the `log` function imported above during
// `bare_bones` // `bare_bones`
($($t:tt)*) => () ($($t:tt)*) => {};
} }
//end of console_log macro //end of console_log macro
#[global_allocator] #[global_allocator]
static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT; static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;
pub fn wasm_instance_init(mize: &mut Mize) -> MizeResult<()> {
pub fn wasm_instance_init(instance: &mut Instance) -> MizeResult<()> {
console_log!("Hello world from wasm_instance_init!!!!!!!!!!"); console_log!("Hello world from wasm_instance_init!!!!!!!!!!");
Ok(()) #[cfg(feature = "target-obsidian")] {
} ////// populate values from $MIZE_CONFIG_FILES
if let Ok(config_file_paths) = obsidian_get_env_var("MIZE_CONFIG_FILES") {
for config_file_path in config_file_paths.split(":") {
println!("reading config file: {config_file_path}");
let content = std::fs::read_to_string(config_file_path)?;
let mut data = ItemData::from_toml(content.as_str())?;
crate::core::item::set_config_from_data(mize, data)?;
}
}
pub fn load_module(instance: &mut Instance, name: &str, path: Option<String>) -> MizeResult<()> { ////// load config from MIZE_CONFIG env var
console_log!("loading module: {}", name); match std::env::var("MIZE_CONFIG") {
Ok(config_string) => {
let data = data_from_string(config_string)?;
crate::core::item::set_config_from_data(mize, data)?;
}
Err(var_err) => match var_err {
VarError::NotPresent => {
debug!("env var MIZE_CONFIG NOT present")
}
VarError::NotUnicode(_) => {
warn!("env var MIZE_CONFIG is not Unicode, so not reading it")
}
},
};
wasm_bindgen_futures::spawn_local(load_module_async(instance.clone(), name.to_owned(), path)); }
Ok(()) Ok(())
} }
async fn load_module_async(mut instance: Instance, name: String, path: Option<String>) -> () { #[cfg(feature = "target-obsidian")]
pub fn obsidian_get_env_var(name: &str) -> Option<String> {
use js_sys::{Reflect, Object};
// 1. Get the global `process` object
let global = js_sys::global();
let process = Reflect::get(&global, &JsValue::from_str("process")).ok()?;
if process.is_undefined() {
return None;
}
// 2. Get the `env` object from `process`
let env = Reflect::get(&process, &JsValue::from_str("env")).ok()?;
if env.is_undefined() {
return None;
}
// 3. Get the `MIZE_STORE` value from `env`
let mize_store = Reflect::get(&env, &JsValue::from_str("MIZE_STORE")).ok()?;
// 4. Convert the JsValue to a Rust String if it exists
mize_store.as_string()
}
pub fn load_module(mize: &mut Mize, name: &str, path: Option<String>) -> MizeResult<()> {
console_log!("loading module NOT IMPLEMENTED: {}", name);
//wasm_bindgen_futures::spawn_local(load_module_async(mize.clone(), name.to_owned(), path));
Ok(())
}
/*
async fn load_module_async(mut mize: Mize, name: String, path: Option<String>) -> () {
let name = name.as_str(); let name = name.as_str();
let module_url = fetch_module(&mut instance, name).unwrap(); let module_url = fetch_module(&mut mize, name).unwrap();
let memory = wasm_bindgen::memory(); let memory = wasm_bindgen::memory();
let mut empty_module: Box<dyn Module + Send + Sync> = Box::new(EmptyModule {}); let mut empty_module: Box<dyn Module + Send + Sync> = Box::new(EmptyModule {});
let mut mize = instance.clone(); let mut mize = mize.clone();
let empty_module_ptr = Box::into_raw(Box::new(empty_module)); let empty_module_ptr = Box::into_raw(Box::new(empty_module));
let empty_module_usize = empty_module_ptr as usize; let empty_module_usize = empty_module_ptr as usize;
@@ -81,7 +128,8 @@ async fn load_module_async(mut instance: Instance, name: String, path: Option<St
console_log!("testttttttttttttt in load_module_async..."); console_log!("testttttttttttttt in load_module_async...");
let function_str = format!(r#" let function_str = format!(
r#"
const promise = new Promise((resolve, reject) => {{ const promise = new Promise((resolve, reject) => {{
import("{module_url}/mize_module_{name}.js").then( module => {{ import("{module_url}/mize_module_{name}.js").then( module => {{
const wasm_bindgen = module.get_wasm_bindgen(); const wasm_bindgen = module.get_wasm_bindgen();
@@ -97,18 +145,23 @@ async fn load_module_async(mut instance: Instance, name: String, path: Option<St
}}) }})
}}) }})
return promise; return promise;
"#); "#
);
let function = Function::new_with_args("memory, empty_module_usize, mize_usize", &function_str); let function = Function::new_with_args("memory, empty_module_usize, mize_usize", &function_str);
let ret_value: JsValue = match function.call3(&JsValue::null(), &memory, &empty_module_usize.into(), &mize_usize.into()) { let ret_value: JsValue = match function.call3(
&JsValue::null(),
&memory,
&empty_module_usize.into(),
&mize_usize.into(),
) {
Ok(val) => val, Ok(val) => val,
Err(err) => { Err(err) => {
console_log!("failed to load the wasm mize module...."); console_log!("failed to load the wasm mize module....");
console_log!("{:?}", err); console_log!("{:?}", err);
return; return;
}, }
}; };
console_log!("got ret_value: {:?}", ret_value); console_log!("got ret_value: {:?}", ret_value);
@@ -119,20 +172,17 @@ async fn load_module_async(mut instance: Instance, name: String, path: Option<St
console_log!("err awaiting ret_promise"); console_log!("err awaiting ret_promise");
console_log!("{:?}", err); console_log!("{:?}", err);
return; return;
}, }
}; };
let mut filled_module = unsafe { let mut filled_module = unsafe { Box::from_raw(empty_module_ptr) };
Box::from_raw(empty_module_ptr)
};
console_log!("before...."); console_log!("before....");
let a = instance.get("0/config"); //let a = mize.get("0/config");
console_log!("after...."); console_log!("after....");
//filled_module.init(&instance); //filled_module.init(&instance);
/* /*
console_log!("got ret_result: {:?}", ret_result); console_log!("got ret_result: {:?}", ret_result);
let ret_f64 = ret_result.as_f64().unwrap(); let ret_f64 = ret_result.as_f64().unwrap();
@@ -148,24 +198,26 @@ async fn load_module_async(mut instance: Instance, name: String, path: Option<St
get_mize_module_fn(&mut module, instance.clone()); get_mize_module_fn(&mut module, instance.clone());
} }
*/ */
} }
*/
pub fn fetch_module(instance: &mut Instance, 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) = instance.get(format!("self/config/module_dir/{}", module_name_with_slashes))?.value_string() { if let Ok(module_path) = mize
.get(format!(
"self/config/module_dir/{}",
module_name_with_slashes
))?
.value_string()
{
return Ok(module_path); return Ok(module_path);
} else { } else {
let module_url = instance.get("self/config/module_url")?.value_string()?; let module_url = mize.get("self/config/module_url")?.value_string()?;
return Ok(format!("{}/{}", module_url, module_name_with_slashes)); return Ok(format!("{}/{}", module_url, module_name_with_slashes));
} }
} }
async fn download_module(instance: &mut Instance, name: &str) -> MizeResult<js_sys::ArrayBuffer> { async fn download_module(mize: &mut Mize, name: &str) -> MizeResult<js_sys::ArrayBuffer> {
use wasm_bindgen_futures::JsFuture; use wasm_bindgen_futures::JsFuture;
use web_sys::{Request, RequestInit, RequestMode, Response}; use web_sys::{Request, RequestInit, RequestMode, Response};
@@ -173,51 +225,55 @@ async fn download_module(instance: &mut Instance, name: &str) -> MizeResult<js_s
opts.set_method("GET"); opts.set_method("GET");
opts.set_mode(RequestMode::Cors); opts.set_mode(RequestMode::Cors);
let url = format!("{}/mize_module_{}_bg.wasm", fetch_module(instance, name)?, name); let url = format!("{}/mize_module_{}_bg.wasm", fetch_module(mize, name)?, name);
let request = Request::new_with_str_and_init(&url, &opts).unwrap(); let request = Request::new_with_str_and_init(&url, &opts).unwrap();
let window = web_sys::window().unwrap(); let window = web_sys::window().unwrap();
let resp_value = JsFuture::from(window.fetch_with_request(&request)).await.unwrap(); let resp_value = JsFuture::from(window.fetch_with_request(&request))
.await
.unwrap();
let resp: Response = resp_value.dyn_into().unwrap(); let resp: Response = resp_value.dyn_into().unwrap();
let wasm_bytes: js_sys::ArrayBuffer = JsFuture::from(resp.array_buffer().unwrap()).await.unwrap().dyn_into().unwrap(); let wasm_bytes: js_sys::ArrayBuffer = JsFuture::from(resp.array_buffer().unwrap())
.await
.unwrap()
.dyn_into()
.unwrap();
Ok(wasm_bytes) Ok(wasm_bytes)
} }
#[wasm_bindgen] #[wasm_bindgen]
pub struct JsInstance { #[derive(Clone)]
inner: NonNull<Instance>, pub struct JsMize {
inner: NonNull<Mize>,
} }
#[wasm_bindgen] #[wasm_bindgen]
pub struct JsItemHandle { pub struct JsItem {
instance: NonNull<Instance>, mize: NonNull<Mize>,
id: MizeId, id: MizeId,
} }
#[wasm_bindgen] #[wasm_bindgen]
pub async fn new_js_instance(config_json_str: String) -> JsInstance { pub async fn new_js_instance(config_json_str: String) -> JsMize {
panic::set_hook(Box::new(console_error_panic_hook::hook)); panic::set_hook(Box::new(console_error_panic_hook::hook));
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 instance = Instance::empty().expect("Instance::empty() failed"); let mut mize = Mize::with_config(config).expect("Mize::new() failed");
instance.set_blocking("0/config", config).expect("Failed to set the config at item 0"); let mut js_instance = JsMize {
inner: NonNull::from(Box::leak(Box::new(mize))),
let mut js_instance = JsInstance { inner: NonNull::from(Box::leak(Box::new(instance))) }; };
return js_instance; return js_instance;
} }
#[wasm_bindgen] #[wasm_bindgen]
impl JsInstance { impl JsMize {
#[wasm_bindgen] #[wasm_bindgen]
pub unsafe fn init(&mut self) -> MizeResult<()> { pub unsafe fn init(&mut self) -> MizeResult<()> {
self.inner.as_mut().init() self.inner.as_mut().init()
@@ -231,43 +287,39 @@ impl JsInstance {
} }
#[wasm_bindgen] #[wasm_bindgen]
pub unsafe fn get_handle(&mut self, id: String) -> MizeResult<JsItemHandle> { pub unsafe fn get_handle(&mut self, id: String) -> MizeResult<JsItem> {
let item = self.inner.as_mut().get(id)?; let item = self.inner.as_mut().get(id)?;
Ok(JsItemHandle { instance: self.inner, id: item.id()}) Ok(JsItem {
} mize: self.inner,
id: item.id(),
} })
impl JsInstance {
pub fn inner(&mut self) -> &mut Instance {
unsafe {
self.inner.as_mut()
}
} }
} }
impl JsMize {
pub fn inner(&mut self) -> &mut Mize {
unsafe { self.inner.as_mut() }
}
}
#[wasm_bindgen] #[wasm_bindgen]
impl JsItemHandle { impl JsItem {
#[wasm_bindgen] #[wasm_bindgen]
pub unsafe fn value_string(&mut self) -> MizeResult<String> { pub unsafe fn value_string(&mut self) -> MizeResult<String> {
let item = self.instance.as_mut().get(self.id.clone())?; let item = self.mize.as_mut().get(self.id.clone())?;
let string = item.value_string()?; let string = item.value_string()?;
Ok(string) Ok(string)
} }
#[wasm_bindgen] #[wasm_bindgen]
pub unsafe fn as_data_full(&mut self) -> MizeResult<JsValue> { pub unsafe fn as_data_full(&mut self) -> MizeResult<JsValue> {
let item = self.instance.as_mut().get(self.id.clone())?; let item = self.mize.as_mut().get(self.id.clone())?;
let data_raw = item.as_data_full()?; let data_raw = item.as_data_full()?;
let jsvalue = serde_wasm_bindgen::to_value(data_raw.cbor())?; let jsvalue = serde_wasm_bindgen::to_value(data_raw.cbor())?;
Ok(jsvalue) Ok(jsvalue)
} }
} }
impl From<MizeError> for JsValue { impl From<MizeError> for JsValue {
fn from(value: MizeError) -> Self { fn from(value: MizeError) -> Self {
let string = format!("{:?}", value); let string = format!("{:?}", value);

View File

@@ -4,17 +4,19 @@ use std::fs::OpenOptions;
use std::io::Write; use std::io::Write;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use sysinfo::{Pid, ProcessRefreshKind, RefreshKind, System}; use sysinfo::{Pid, ProcessRefreshKind, RefreshKind, System};
use tracing::debug;
use crate::core::id::MizeId; use crate::id::MizeId;
use crate::core::item::{Item, ItemData}; use crate::item::{Item, ItemData};
use crate::error::{IntoMizeResult, MizeError, MizeResult}; use crate::error::{IntoMizeResult, MizeError, MizeResult};
use crate::instance::store::IdIter; use crate::instance::store::IdIter;
use crate::instance::{self, Mize}; use crate::instance::{self, Mize};
use crate::item::get_raw_from_cbor; use crate::item::get_raw_from_cbor;
use crate::item::IntoItemData; use crate::item::IntoItemData;
use crate::memstore::MemStore; use crate::memstore::MemStore;
use crate::{core::instance::store::Store, mize_err}; use crate::{instance::store::Store, mize_err};
use crate::prelude::*;
#[cfg(target_family = "unix")]
use tokio::net::UnixStream; use tokio::net::UnixStream;
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
@@ -72,6 +74,7 @@ impl FileStore {
} }
} }
#[cfg(target_family = "unix")]
async fn try_connect_to_sock(store_path: String) -> MizeResult<()> { async fn try_connect_to_sock(store_path: String) -> MizeResult<()> {
match UnixStream::connect(PathBuf::from(store_path).join("sock")).await { match UnixStream::connect(PathBuf::from(store_path).join("sock")).await {
Err(_) => Err(mize_err!("")), Err(_) => Err(mize_err!("")),
@@ -79,15 +82,30 @@ async fn try_connect_to_sock(store_path: String) -> MizeResult<()> {
} }
} }
#[cfg(target_family = "windows")]
async fn try_connect_to_sock(store_path: String) -> MizeResult<()> {
use tokio::net::windows::named_pipe::{ClientOptions};
let pipe_name = format!(
"\\.\\pipe\\mize_{}",
store_path.replace('\\', "_").replace('/', "_").replace(':', "_")
);
// ClientOptions::new().open() is synchronous, not async
// So we just try to open and immediately close it
match ClientOptions::new().open(&pipe_name) {
Err(_) => Err(mize_err!("")),
Ok(_) => Ok(()),
}
}
impl Store for FileStore { impl Store for FileStore {
fn new_id(&self) -> MizeResult<String> { fn new_id(&self) -> MizeResult<String> {
let mut next_id: u64 = let mut next_id: u64 =
String::from_utf8(fs::read(self.path.join("next_id")).mize_result_msg(format!( String::from_utf8(fs::read(self.path.join("next_id")).mres_msg(format!(
"could not read next_id at '{}'", "could not read next_id at '{}'",
self.path.display() self.path.display()
))?)? ))?)?
.parse() .parse()
.mize_result_msg(format!( .mres_msg(format!(
"could not parse next_id at '{}' to u64", "could not parse next_id at '{}' to u64",
self.path.display() self.path.display()
))?; ))?;
@@ -132,7 +150,7 @@ impl Store for FileStore {
let file = OpenOptions::new().read(true).create(true).open(path)?; let file = OpenOptions::new().read(true).create(true).open(path)?;
let cbor_val: CborValue = ciborium::from_reader(file).mize_result_msg(format!( let cbor_val: CborValue = ciborium::from_reader(file).mres_msg(format!(
"could not read file '{}' from FileStore", "could not read file '{}' from FileStore",
self.path.join("store").join(id.store_part()).display() self.path.join("store").join(id.store_part()).display()
))?; ))?;
@@ -163,17 +181,20 @@ impl Store for FileStore {
) )
})?; })?;
let cbor_value: CborValue = ciborium::from_reader(file).mize_result_msg(format!( let cbor_value: CborValue = ciborium::from_reader(file).mres_msg(format!(
"could not read file '{}' from FileStore", "could not read file '{}' from FileStore",
self.path.join("store").join(id.store_part()).display() self.path.join("store").join(id.store_part()).display()
))?; ))?;
let data: ItemData = cbor_value.into_item_data(); let data: ItemData = cbor_value.into_item_data();
let tmp = id.path(); let tmp = id.path();
let mut path_iter = tmp.into_iter(); let mut path_iter = tmp.iter();
path_iter.next(); path_iter.next();
let new_path: Vec<String> = path_iter.map(|v| v.to_owned()).collect(); let new_path: Vec<String> = path_iter.map(|v| v.to_owned()).collect();
//println!("data: {}", data);
//println!("new_path: {:?}", new_path);
let ret_data = data.get_path(new_path)?; let ret_data = data.get_path(new_path)?;
//println!("ret_data: {}", ret_data);
return Ok(ret_data); return Ok(ret_data);
} }
@@ -188,6 +209,7 @@ impl Store for FileStore {
} }
} }
#[cfg(target_family = "unix")]
fn valid_pid_file(path: &Path) -> MizeResult<Option<u32>> { fn valid_pid_file(path: &Path) -> MizeResult<Option<u32>> {
let pid_file_path = path.join("pid"); let pid_file_path = path.join("pid");
@@ -197,12 +219,12 @@ fn valid_pid_file(path: &Path) -> MizeResult<Option<u32>> {
} }
// read pid file // read pid file
let pid: u32 = String::from_utf8(fs::read(&pid_file_path).mize_result_msg(format!( let pid: u32 = String::from_utf8(fs::read(&pid_file_path).mres_msg(format!(
"Could not read contents of pid file at '{}'", "Could not read contents of pid file at '{}'",
pid_file_path.display() pid_file_path.display()
))?)? ))?)?
.parse() .parse()
.mize_result_msg(format!( .mres_msg(format!(
"Could not parse contents of pid file at '{}' to u32", "Could not parse contents of pid file at '{}' to u32",
pid_file_path.display() pid_file_path.display()
))?; ))?;

View File

@@ -0,0 +1,414 @@
use clap::{crate_version, Arg, Command};
use clap::{ArgAction, ArgMatches};
use std::collections::HashMap;
use std::path::PathBuf;
use colored::Colorize;
use core::fmt;
use std::env;
use std::fmt::Write;
use std::sync::atomic::AtomicUsize;
use std::sync::Arc;
use std::sync::Mutex;
use tokio::sync::mpsc;
use tracing::level_filters::LevelFilter;
use tracing::{Dispatch, Event};
use tracing::{field::Visit, Id, Subscriber};
use tracing::{span_enabled, Instrument, Level};
use tracing_core::Field;
use tracing_subscriber::filter::EnvFilter;
use tracing_subscriber::fmt::format;
use tracing_subscriber::fmt::FmtContext;
use tracing_subscriber::fmt::FormatEvent;
use tracing_subscriber::fmt::FormatFields;
use tracing_subscriber::fmt::FormattedFields;
use tracing_subscriber::registry::LookupSpan;
use crate::id::MizeId;
use crate::item::ItemData;
use crate::MizeError;
use crate::MizeResult;
use crate::prelude::*;
static DEFAULT_LOG_LEVEL: LevelFilter = LevelFilter::WARN;
/*
#[macro_export]
macro_rules! trace {
($($arg:tt)*) => {
// format_args! handles string interpolation safely at compile time
if let Err(e) = $crate::write_to_mize_stdout(format_args!("[ {} ] {}\n", colored::Colorize::yellow("TRACE"), format_args!($($arg)*))) {
// Choose your error handling behavior here (panic, print to stderr, or ignore)
eprintln!("out! macro failed to write!!");
}
};
}
#[macro_export]
macro_rules! debug {
($($arg:tt)*) => {
// format_args! handles string interpolation safely at compile time
if let Err(e) = $crate::write_to_mize_stdout(format_args!("[ {} ] {}\n", colored::Colorize::green("DEBUG"), format_args!($($arg)*))) {
// Choose your error handling behavior here (panic, print to stderr, or ignore)
eprintln!("out! macro failed to write!!");
}
};
}
#[macro_export]
macro_rules! info {
($($arg:tt)*) => {
// format_args! handles string interpolation safely at compile time
if let Err(e) = $crate::write_to_mize_stdout(format_args!("[ {} ] {}\n", colored::Colorize::blue("INFO"), format_args!($($arg)*))) {
// Choose your error handling behavior here (panic, print to stderr, or ignore)
eprintln!("out! macro failed to write!!");
}
};
}
#[macro_export]
macro_rules! warn {
($($arg:tt)*) => {
// format_args! handles string interpolation safely at compile time
if let Err(e) = $crate::write_to_mize_stdout(format_args!("[ {} ] {}\n", colored::Colorize::truecolor("WARN", 245, 164, 66), format_args!($($arg)*))) {
// Choose your error handling behavior here (panic, print to stderr, or ignore)
eprintln!("out! macro failed to write!!");
}
};
}
#[macro_export]
macro_rules! error {
($($arg:tt)*) => {
// format_args! handles string interpolation safely at compile time
if let Err(e) = $crate::write_to_mize_stdout(format_args!("[ {} ] {}\n", colored::Colorize::red("ERROR"), format_args!($($arg)*))) {
// Choose your error handling behavior here (panic, print to stderr, or ignore)
eprintln!("out! macro failed to write!!");
}
};
}
*/
pub fn init_logger(cli_matches: Option<&ArgMatches>) {
trace!("init_logger called");
// to save log messages, before the logger is setup
let mut log_messages: Vec<(Level, String)> = Vec::new();
log_messages.push((Level::INFO, "Starting Mize".to_owned()));
// builder with DEFAULT_LOG_LEVEL
let mut builder = tracing_subscriber::fmt::Subscriber::builder();
// set loglevel
let mut log_level = DEFAULT_LOG_LEVEL;
log_messages.push((
Level::TRACE,
format!(
"set log level from DEFAULT_LOG_LEVEL to {}",
DEFAULT_LOG_LEVEL
),
));
// check env RUST_LOG
let mut val_rust_log = level_from_env("RUST_LOG", log_level);
if let Some(level_rust_log) = val_rust_log.0 {
log_level = level_rust_log;
log_messages.push((
Level::TRACE,
format!(
"set log level from the Variable RUST_LOG to {}",
level_rust_log
),
));
}
log_messages.append(&mut val_rust_log.1);
// check env LOG
let mut val_mize_log = level_from_env("LOG", log_level);
if let Some(level_mize_log) = val_mize_log.0 {
debug!("set log level from the Variable LOG to {}", level_mize_log);
log_level = level_mize_log;
log_messages.push((
Level::TRACE,
format!(
"set log level from the Variable MIZE_LOG to {}",
level_mize_log
),
));
}
log_messages.append(&mut val_mize_log.1);
// check env MIZE_LOG
let mut val_mize_log = level_from_env("MIZE_LOG", log_level);
if let Some(level_mize_log) = val_mize_log.0 {
log_level = level_mize_log;
log_messages.push((
Level::TRACE,
format!(
"set log level from the Variable MIZE_LOG to {}",
level_mize_log
),
));
}
log_messages.append(&mut val_mize_log.1);
// check -vvvvv flags
if let Some(cli_matches) = cli_matches {
let v_flag_count = cli_matches.get_count("verbose");
if v_flag_count > 0 {
let default_log_level_as_num = match DEFAULT_LOG_LEVEL {
LevelFilter::OFF => 0,
LevelFilter::ERROR => 1,
LevelFilter::WARN => 2,
LevelFilter::INFO => 3,
LevelFilter::DEBUG => 4,
LevelFilter::TRACE => 5,
};
let new_log_level_num = default_log_level_as_num + v_flag_count;
log_level = match new_log_level_num {
0 => LevelFilter::OFF,
1 => LevelFilter::ERROR,
2 => LevelFilter::WARN,
3 => LevelFilter::INFO,
4 => LevelFilter::DEBUG,
5 => LevelFilter::TRACE,
_ => {
log_messages.push((
Level::TRACE,
format!(
"the number of verbose (-v) flags passed ({}) exeeds the maximumg log_level",
v_flag_count
),
));
LevelFilter::TRACE
}
};
log_messages.push((
Level::TRACE,
format!("set log level from the verbose flags to {}", log_level),
));
}
// check --log-level arg
if let Some(log_level_arg_string) = cli_matches.get_one::<String>("log-level") {
let log_level_from_arg = match log_level_arg_string.to_owned().to_lowercase().as_str() {
"none" => LevelFilter::OFF,
"trace" => LevelFilter::TRACE,
"debug" => LevelFilter::DEBUG,
"info" => LevelFilter::INFO,
"warn" => LevelFilter::WARN,
"error" => LevelFilter::ERROR,
_ => {
log_messages.push((Level::WARN, format!(
"The argument to --log-level \"{}\" is not a valid log-level. loglevel stays at: {}"
, log_level_arg_string
, log_level
)));
log_level
}
};
log_level = log_level_from_arg;
log_messages.push((
Level::TRACE,
format!(
"set log level from the --log-level arg to {}",
log_level_from_arg
),
));
}
}
// actually set the log_level on the builder
log_messages.push((Level::INFO, format!("loglevel is now {}", log_level)));
// filter loging
//let mut my_env_filter = tracing_subscriber::filter::EnvFilter::new("")
//.add_directive("[get_raw_from_cbor]=debug".parse().expect("error parsing log directive"))
//.add_directive("[MemStore::get_value_raw]=trace".parse().expect("error parsing log directive"))
//.add_directive("trace".parse().unwrap())
//.add_directive("[get_raw_from_cbor]=debug".parse().expect("error parsing log directive"))
//;
//MinimalTracer::register();
debug!("log_level: {}", log_level);
let field_formatter = tracing_subscriber::fmt::format::debug_fn(|writer, field, value| {
if field.name() == "message" {
write!(writer, "{:?}", value)?;
} else {
write!(writer, "{}={:?}", field, value)?;
}
Ok(())
});
let subscriber = builder
//.with_env_filter(my_env_filter)
.without_time()
.event_format(MyFormatter)
.fmt_fields(field_formatter)
.with_max_level(log_level)
.finish();
let dispatch = Dispatch::new(subscriber);
if let Err(e) = tracing::dispatcher::set_global_default(dispatch) {
println!("faild to set_global_default(dispatch): {e}");
}
// write out all log messages from log_messages
for (level, msg) in log_messages {
match level {
Level::ERROR => error!("{}", msg),
Level::WARN => warn!("{}", msg),
Level::INFO => info!("{}", msg),
Level::DEBUG => debug!("{}", msg),
Level::TRACE => trace!("{}", msg),
}
}
}
fn level_from_env(
var_name: &str,
log_level: LevelFilter,
) -> (Option<LevelFilter>, Vec<(Level, String)>) {
let mut log_messages: Vec<(Level, String)> = Vec::new();
let var_value = env::var(var_name);
debug!("checking env var: {}={:?}", var_name, var_value);
match var_value {
Ok(val) => {
// set loglevel acording to RUST_LOG
let level = match val.to_lowercase().as_str() {
"none" => LevelFilter::OFF,
"trace" => LevelFilter::TRACE,
"debug" => LevelFilter::DEBUG,
"info" => LevelFilter::INFO,
"warn" => LevelFilter::WARN,
"error" => LevelFilter::ERROR,
_ => {
log_messages.push((Level::WARN, format!(
"The value of Environment Variable {}: \"{}\" is not a valid log-level. loglevel stays at: {}"
, var_name
, val
, log_level
)));
log_level
}
};
return (Some(level), log_messages);
}
Err(env::VarError::NotPresent) => {}
Err(env::VarError::NotUnicode(os_string)) => {
let os_string_as_hex: String = os_string
.clone()
.into_encoded_bytes()
.iter()
.map(|b| format!("{:02x}", b).to_string())
.collect::<Vec<String>>()
.join("");
log_messages.push((Level::DEBUG, format!(
"value of Environment Variable {} was not a unicode valid string. Therefore ignoring it.
\n value with replacement char U+FFFD: {}
\n the WTF-8 raw bytes as hex: {}"
, var_name
, os_string.to_string_lossy(), os_string_as_hex
)));
}
};
return (None, log_messages);
}
struct MyFormatter;
impl<S, N> FormatEvent<S, N> for MyFormatter
where
S: tracing::Subscriber + for<'a> LookupSpan<'a>,
N: for<'a> FormatFields<'a> + 'static,
{
fn format_event(
&self,
ctx: &FmtContext<'_, S, N>,
mut writer: format::Writer<'_>,
event: &Event<'_>,
) -> fmt::Result {
// Format values from the event's's metadata:
let metadata = event.metadata();
// set format
//builder.format(|buf, record| match record.level() {});
let level_str = match metadata.level() {
&Level::TRACE => format!("[ {} ]", "TRACE".yellow()),
&Level::DEBUG => format!("[ {} ]", "DEBUG".green()),
&Level::INFO => format!("[ {} ]", "INFO".blue()),
&Level::WARN => format!("[ {} ]", "WARN".truecolor(245, 164, 66)),
&Level::ERROR => format!("[ {} ]", "ERROR".red()),
};
// with target
// write!(&mut writer, "{} {}: ", level_str, metadata.target())?;
write!(&mut writer, "{} ", level_str)?;
// Format all the spans in the event's span context.
if let Some(scope) = ctx.event_scope() {
for span in scope.from_root() {
write!(writer, "{}", span.name())?;
// `FormattedFields` is a formatted representation of the span's
// fields, which is stored in its extensions by the `fmt` layer's
// `new_span` method. The fields will have been formatted
// by the same field formatter that's provided to the event
// formatter in the `FmtContext`.
let ext = span.extensions();
let fields = &ext
.get::<FormattedFields<N>>()
.expect("will never be `None`");
// Skip formatting the fields if the span had no fields.
if !fields.is_empty() {
//write!(writer, "{{{}}}", fields)?;
}
write!(writer, ": ")?;
}
}
// Write fields on the event
ctx.field_format().format_fields(writer.by_ref(), event)?;
writeln!(writer);
Ok(())
}
}
// thanks: https://stackoverflow.com/a/75546824
pub struct StringVisitor<'a> {
string: &'a mut String,
}
impl<'a> StringVisitor<'a> {
pub(crate) fn new(string: &'a mut String) -> Self {
StringVisitor { string }
}
}
impl<'a> Visit for StringVisitor<'a> {
fn record_debug(&mut self, field: &Field, value: &dyn fmt::Debug) {
if field.name() == "message" {
write!(self.string, "{value:?} ").unwrap();
} else {
write!(self.string, "{} = {:?}; ", field.name(), value).unwrap();
}
}
}
struct LogFilter {
target: Option<String>,
level: Option<Level>,
}
fn string_to_level(string: &str) -> Option<Level> {
match string.to_lowercase().as_str() {
"info" => Some(Level::INFO),
"debug" => Some(Level::DEBUG),
"warn" | "warning" => Some(Level::WARN),
"trace" => Some(Level::TRACE),
"error" => Some(Level::ERROR),
_ => None,
}
}

653
core/src/platform/os/mod.rs Normal file
View File

@@ -0,0 +1,653 @@
use clap::ArgMatches;
use colored::Colorize;
use tracing::Dispatch;
use core::fmt;
use flate2::bufread::GzDecoder;
use sha2::{Digest, Sha256};
use std::env::VarError;
use std::fmt::Write;
use std::fs;
use std::fs::File;
use std::path::Path;
use std::path::PathBuf;
use std::time::UNIX_EPOCH;
use tar::Archive;
use tracing::Event;
use tracing::field::Visit;
use tracing::Level;
use tracing_core::Field;
use tracing_subscriber::fmt::format;
use tracing_subscriber::fmt::FmtContext;
use tracing_subscriber::fmt::FormatEvent;
use tracing_subscriber::fmt::FormatFields;
use tracing_subscriber::fmt::FormattedFields;
use tracing_subscriber::registry::LookupSpan;
use std::sync::Arc;
use std::sync::Mutex;
use crate::prelude::*;
use crate::item::data_from_string;
use self::fsstore::FileStore;
pub mod fsstore;
pub mod logging;
#[cfg(target_family = "unix")]
mod unix_socket;
#[cfg(target_family = "windows")]
mod windows_socket;
#[cfg(target_family = "unix")]
use crate::platform::os::unix_socket::UnixListener;
#[cfg(target_family = "windows")]
use crate::platform::os::windows_socket::WindowsListener as UnixListener;
//mod web;
pub fn os_instance_init(mize: &mut Mize) -> MizeResult<()> {
// this is the code, that runs to initialize an Instance on a system with an os present.
// setup loging to stderr if enabled
if mize
.get_config("log.stderr.enable")
.unwrap_or(ItemData::new())
.value_bool()
.unwrap_or(false)
{
use tracing::Level;
let level_string = mize
.get_config("log.level")?
.value_string()
.unwrap_or("info".to_string());
let log_level = match level_string.to_lowercase().as_str() {
"info" => Level::INFO,
"debug" => Level::DEBUG,
"warn" | "warning" => Level::WARN,
"trace" => Level::TRACE,
"error" => Level::ERROR,
_ => Level::INFO, // the default log level
};
println!("enabling loggging to stderr with log_level: {}", log_level);
// builder
let builder = tracing_subscriber::fmt::Subscriber::builder();
let field_formatter = tracing_subscriber::fmt::format::debug_fn(|writer, field, value| {
if field.name() == "message" {
write!(writer, "{:?}", value)?;
} else {
//write!(writer, "{}={:?}", field, value)?;
}
Ok(())
});
builder
//.with_env_filter(my_env_filter)
.without_time()
.event_format(MyFormatter)
.fmt_fields(field_formatter)
.with_max_level(log_level)
.init();
}
////// if a config.store_path is set, upgrade to the filestore there
let store_path = match mize.get_config("store")?.value_string() {
Ok(path) => path,
Err(_e) => {
// the default store_path: $HOME/.mize
let home_dir = if let Some(dir) = std::env::home_dir() {
dir
} else {
return Err(mize_err!(
"could not get home_dir and store_path is not set in the config"
));
};
let store_path_buff = home_dir.join(".mize");
let store_path = store_path_buff.display().to_string();
mize.set_blocking(
"self/config/store_path",
store_path.clone().into_item_data(),
)?;
let store_path = mize.get("self/config/store_path")?.value_string()?;
println!("store_path: {}", store_path);
store_path
}
};
if FileStore::store_is_opened(store_path.to_owned(), mize)? {
// if the store is already opened, connect to the instance, that opened it and join
// it's namespace
#[cfg(target_family = "unix")]
{
info!("CONNECTING");
use crate::platform::os::unix_socket;
unix_socket::connect(mize, store_path.into())?;
return Ok(());
}
#[cfg(target_family = "windows")]
{
info!("CONNECTING");
use crate::platform::os::windows_socket;
windows_socket::connect(mize, store_path.into())?;
return Ok(());
}
} else {
// else open it ourselves
let file_store = FileStore::new(store_path.as_str())?;
mize.migrate_to_store(Box::new(file_store))?;
let path = Path::new(&store_path).to_owned();
#[cfg(target_family = "unix")]
{
mize.add_conn_listener(UnixListener::new(path)?)?;
}
#[cfg(target_family = "windows")]
{
mize.add_conn_listener(UnixListener::new(path)?)?;
}
}
Ok(())
}
pub fn seconds_since_modification(path: &Path) -> MizeResult<u64> {
let metadata = fs::metadata(path)?;
let modified = metadata.modified()?;
let duration = modified.duration_since(UNIX_EPOCH)?;
Ok(duration.as_secs())
}
/// Best-effort rustc target triple for the currently running binary.
pub fn host_rust_target() -> &'static str {
#[cfg(all(target_arch = "x86_64", target_os = "linux", target_env = "gnu"))]
{
"x86_64-unknown-linux-gnu"
}
#[cfg(all(target_arch = "aarch64", target_os = "linux", target_env = "gnu"))]
{
"aarch64-unknown-linux-gnu"
}
#[cfg(all(target_arch = "x86_64", target_os = "macos"))]
{
"x86_64-apple-darwin"
}
#[cfg(all(target_arch = "aarch64", target_os = "macos"))]
{
"aarch64-apple-darwin"
}
#[cfg(all(target_arch = "x86_64", target_os = "windows"))]
{
"x86_64-pc-windows-gnu"
}
#[cfg(all(target_arch = "aarch64", target_os = "windows"))]
{
"aarch64-pc-windows-gnullvm"
}
#[cfg(not(any(
all(target_arch = "x86_64", target_os = "linux", target_env = "gnu"),
all(target_arch = "aarch64", target_os = "linux", target_env = "gnu"),
all(target_arch = "x86_64", target_os = "macos"),
all(target_arch = "aarch64", target_os = "macos"),
all(target_arch = "x86_64", target_os = "windows"),
all(target_arch = "aarch64", target_os = "windows"),
)))]
{
"unknown"
}
}
/// Dist artifact filename: `{hash}-{part_id}-cd.tar.gz` or `{hash}-{part_id}-{version}.tar.gz`.
pub fn part_dist_artifact_name(hash: &str, part_id: &str, version: &str, cd: bool) -> String {
if cd {
format!("{hash}-{part_id}-cd.tar.gz")
} else {
format!("{hash}-{part_id}-{version}.tar.gz")
}
}
pub fn parts_dist_cd(mize: &mut Mize) -> bool {
mize.get_config("parts.dist.cd")
.ok()
.and_then(|d| d.value_bool().ok())
.unwrap_or(false)
}
/// Read `version` from a Cargo.toml's `[package]` table.
pub fn cargo_package_version(cargo_toml_path: &str) -> MizeResult<String> {
let contents = fs::read_to_string(cargo_toml_path)
.map_err(|e| mize_err!("could not read {cargo_toml_path}: {e}"))?;
let data = ItemData::from_toml(&contents)?;
data.get_path("package.version")?
.value_string()
.map_err(|e| e.msg(format!("no package.version in {cargo_toml_path}")))
}
/// Resolve a part's cargo version for dist URLs (non-cd).
pub fn resolve_part_version(mize: &mut Mize, part_name: &str) -> MizeResult<String> {
if let Ok(v) = mize.get_config(&format!("{part_name}.version")) {
if let Ok(s) = v.value_string() {
if !s.is_empty() {
return Ok(s);
}
}
}
if part_name == "mize" {
return Ok(env!("CARGO_PKG_VERSION").to_owned());
}
if let Ok(workspaces) = mize.get_config("parts.workspaces") {
if let Some(arr) = workspaces.cbor().as_array() {
for src in arr {
let Some(dir) = src.as_text() else { continue };
for candidate in [
format!("{dir}/{part_name}/Cargo.toml"),
format!("{dir}/mize.{part_name}/Cargo.toml"),
] {
if PathBuf::from(&candidate).exists() {
return cargo_package_version(&candidate);
}
}
}
}
}
if let Ok(mize_src) = mize.get_config("mize.src").and_then(|d| d.value_string()) {
for candidate in [
format!("{mize_src}/parts/{part_name}/Cargo.toml"),
format!("{mize_src}/parts/mize.{part_name}/Cargo.toml"),
format!("{mize_src}/core/Cargo.toml"),
] {
if part_name != "mize" && candidate.ends_with("core/Cargo.toml") {
continue;
}
if PathBuf::from(&candidate).exists() {
return cargo_package_version(&candidate);
}
}
}
Err(mize_err!(
"could not resolve cargo version for part '{part_name}' (set {part_name}.version or parts.workspaces, or enable parts.dist.cd)"
))
}
fn load_selector_base(mize: &mut Mize) -> ItemData {
// Prefer config opt; fall back to store path used by tests / older setups.
if let Ok(sel) = mize.get("self/config/selector").and_then(|i| i.as_data_full()) {
if !sel.cbor().is_null() {
return sel;
}
}
mize.get("0/config/selector")
.and_then(|i| i.as_data_full())
.unwrap_or_else(|_| ItemData::new())
}
pub fn get_part_hash(
mize: &mut Mize,
name: &str,
options: ItemData,
) -> MizeResult<(String, String)> {
let mut selector_data = load_selector_base(mize);
if selector_data
.get_path("mize_version")
.ok()
.and_then(|v| v.value_string().ok())
.filter(|s| !s.is_empty())
.is_none()
{
selector_data.set_path("mize_version", env!("CARGO_PKG_VERSION"))?;
}
if selector_data
.get_path("system")
.ok()
.and_then(|v| v.value_string().ok())
.filter(|s| !s.is_empty())
.is_none()
{
selector_data.set_path("system", host_rust_target())?;
}
// handle the case, when we want a part for not the system we are running on...
debug!("get_part_hash: name: {}", name);
let mut name_parts = name.split(".");
if name_parts
.next()
.ok_or(mize_err!("no 0th element in the modName"))?
== "cross"
{
let system = name_parts
.next()
.ok_or(mize_err!("no 1th element in the modName"))?;
let mod_name: String = name_parts.collect();
println!("get_part_hash: mod_name: {}", mod_name);
if mod_name == "" {
return Err(mize_err!("no 2nd element in the modName"));
}
selector_data.set_path("system", system)?;
selector_data.set_path("modName", mod_name)?;
} else {
selector_data.set_path("modName", name)?;
}
selector_data.merge(options);
// important to produce the same hash for the sama selector data
selector_data.sort_keys();
let selector_str = selector_data.to_json()?;
let mut hasher = Sha256::new();
hasher.update(selector_str.clone().as_bytes());
let result = hasher.finalize();
let mut hex: String = result
.iter()
.map(|b| format!("{:02x}", b).to_string())
.collect::<Vec<String>>()
.join("");
hex.truncate(32);
return Ok((hex, selector_str));
}
pub fn fetch_part(mize: &mut Mize, part_name: &str) -> MizeResult<String> {
let (part_hash, part_selector) = get_part_hash(mize, part_name, ItemData::new())?;
let cd = parts_dist_cd(mize);
let version = if cd {
String::new()
} else {
resolve_part_version(mize, part_name)?
};
let artifact = part_dist_artifact_name(&part_hash, part_name, &version, cd);
let part_url = format!("https://dist.mize.works/{artifact}");
let store_path = mize.get_config("store")?.value_string()?;
let parts_path = format!("{store_path}/parts");
let target_dir = format!("{parts_path}/{part_hash}-{part_name}");
let tmp_file_path_gz = format!("{parts_path}/{artifact}");
std::fs::create_dir_all(&parts_path)?;
let tmp_gz_file = if !PathBuf::from(tmp_file_path_gz.clone()).exists() {
let mut tmp_gz_file = File::create(&tmp_file_path_gz)?;
debug!(
"downloading part '{}' from {} (selector: {})",
part_name, part_url, part_selector
);
let response = http_req::request::get(&part_url, &mut tmp_gz_file)?;
let code = response.status_code();
if code != 200.into() {
return Err(mize_err!(
"failed to get part '{}' with hash '{}' and selector: {}",
part_name,
part_hash,
part_selector
)
.msg(format!(
"the http request to fetch the part from '{}' returned a status code of: {}",
part_url, code
)));
}
tmp_gz_file
} else {
std::fs::File::open(tmp_file_path_gz.clone())?
};
let tar = GzDecoder::new(std::io::BufReader::new(tmp_gz_file));
let mut archive = Archive::new(tar);
//archive.set_mask(umask::Mode::parse("rwxrwxrwx")?.into());
std::fs::create_dir_all(target_dir.as_str())?;
archive.unpack(target_dir.as_str())?;
fs::remove_file(&tmp_file_path_gz)?;
Ok(format!("{parts_path}/{part_hash}-{part_name}"))
}
fn get_part_path(mize: &mut Mize, part_name: &str, _path: Option<String>) -> MizeResult<String> {
let part_symbol_name = part_name.replace(".", "_");
// check if $name.src is set
if let Ok(itm) = mize.get_config(format!("{part_name}.src").as_str()) {
let part_src = itm.value_string().map_err(|e| e.msg(format!("opt {part_name}.src was not a string")))?;
// will only work for rust for now
let part_path = format!("{part_src}/target/debug/libmize_part_{part_symbol_name}.so");
return Ok(part_path);
}
// check if $name.out is set
if let Ok(itm) = mize.get_config(format!("{part_name}.out").as_str()) {
let part_out = itm.value_string().map_err(|e| e.msg(format!("opt {part_name}.out was not a string")))?;
let part_path = format!("{part_out}/lib/libmize_part_{part_symbol_name}.so");
return Ok(part_path);
}
// check if we find the part inside mize.src
if let Ok(itm) = mize.get_config("mize.src") {
let mize_src = itm.value_string().map_err(|e| e.msg("opt mize.src was not a string"))?;
let part_path = format!("{mize_src}/target/debug/libmize_part_{part_symbol_name}.so");
return Ok(part_path);
}
// fetch the part from https://mize.works
let out_path = fetch_part(mize, part_name)?;
return Ok(format!("{out_path}/lib/libmize_part_{part_symbol_name}.so"));
//Err(mize_err!("part {part_name} not found"))
}
/// Load a part `.so` for `part_name`, registering it under `at_id` (or `part_name` if None).
/// Uses `get_global_mize()` so nested loads from inside another part's init work.
pub fn load_part(part_name: &str, path: Option<String>, at_id: Option<&str>) -> MizeResult<()> {
let mut mize = crate::get_global_mize()?;
let part_path = get_part_path(&mut mize, part_name, path)?;
let lib = unsafe { libloading::Library::new(part_path)? };
unsafe {
if let Ok(rust_hack_func) = lib.get::<libloading::Symbol<unsafe extern "C" fn(Arc<Mutex<Mize>>, *const std::ffi::c_void)>>("mize_rust_hack_set_global".as_bytes()) {
tracing::dispatcher::get_default(|dispatch| {
rust_hack_func(GLOBAL_MIZE.get().unwrap().clone(), dispatch as *const Dispatch as *const std::ffi::c_void);
});
};
}
let part_symbol_name = part_name.replace(".", "_");
let func: libloading::Symbol<unsafe extern "C" fn()> =
unsafe { lib.get(format!("part_{}", part_symbol_name).as_bytes())? };
let eval_name = at_id.unwrap_or(part_name);
let part_id = mize.clone().id_new_dots(eval_name)?;
{
let mut opts = mize.config_opts.lock()?;
opts.entry(part_id.clone()).or_insert_with(|| ConfigOpt {
name: eval_name.to_owned(),
val: Some(ItemData::new()),
fun: None,
deps: Vec::new(),
});
}
let prev_eval = mize.clone().cur_eval_part()?;
mize.clone().set_cur_eval_part(Some(part_id))?;
crate::c_api::clear_err();
unsafe { func() };
let c_err = crate::c_api::take_err();
mize.clone().set_cur_eval_part(prev_eval)?;
// dropping the lib, would (i suspect) free all memory, of the library's code, which would
// make the part's vtable point into empty memory
std::mem::forget(lib);
if let Some(err) = c_err {
return Err(err);
}
Ok(())
}
pub fn config_from_cli_args(matches: &ArgMatches) -> MizeResult<ItemData> {
let mut config = ItemData::new();
////// populate values from $MIZE_CONFIG_FILES
if let Ok(config_file_paths) = std::env::var("MIZE_CONFIG_FILES") {
for config_file_path in config_file_paths.split(":") {
println!("reading config file: {config_file_path}");
let content = std::fs::read_to_string(config_file_path)?;
let data = ItemData::from_toml(content.as_str())?;
config.merge(data);
}
}
////// load config from MIZE_CONFIG env var
match std::env::var("MIZE_CONFIG") {
Ok(config_string) => {
let data = data_from_string(config_string)?;
config.merge(data);
}
Err(var_err) => match var_err {
VarError::NotPresent => {
debug!("env var MIZE_CONFIG NOT present")
}
VarError::NotUnicode(_) => {
warn!("env var MIZE_CONFIG is not Unicode, so not reading it")
}
},
};
if let Some(config_file_path) = matches.get_one::<String>("config") {
config.merge(config_from_file(config_file_path.to_string())?);
trace!("config after --config arg: {}", config);
}
if let Some(config_string) = matches.get_one::<String>("opt") {
config.merge(data_from_string(config_string.to_string())?);
trace!("config after --opt arg: {}", config);
}
return Ok(config);
}
pub fn config_from_file(file_path: String) -> MizeResult<ItemData> {
let toml_string = fs::read_to_string(&file_path)
.mres_msg(format!("Could not read file {} as mize config", &file_path))?;
let item_data = ItemData::from_toml(toml_string.as_str())?;
return Ok(item_data);
}
struct MyFormatter;
impl<S, N> FormatEvent<S, N> for MyFormatter
where
S: tracing::Subscriber + for<'a> LookupSpan<'a>,
N: for<'a> FormatFields<'a> + 'static,
{
fn format_event(
&self,
ctx: &FmtContext<'_, S, N>,
mut writer: format::Writer<'_>,
event: &Event<'_>,
) -> fmt::Result {
// Format values from the event's's metadata:
let metadata = event.metadata();
// set format
//builder.format(|buf, record| match record.level() {});
let level_str = match metadata.level() {
&Level::TRACE => format!("[ {} ]", "TRACE".yellow()),
&Level::DEBUG => format!("[ {} ]", "DEBUG".green()),
&Level::INFO => format!("[ {} ]", "INFO".blue()),
&Level::WARN => format!("[ {} ]", "WARN".truecolor(245, 164, 66)),
&Level::ERROR => format!("[ {} ]", "ERROR".red()),
};
// with target
// write!(&mut writer, "{} {}: ", level_str, metadata.target())?;
write!(&mut writer, "{} ", level_str)?;
// Format all the spans in the event's span context.
if let Some(scope) = ctx.event_scope() {
for span in scope.from_root() {
write!(writer, "{}", span.name())?;
// `FormattedFields` is a formatted representation of the span's
// fields, which is stored in its extensions by the `fmt` layer's
// `new_span` method. The fields will have been formatted
// by the same field formatter that's provided to the event
// formatter in the `FmtContext`.
let ext = span.extensions();
let fields = &ext
.get::<FormattedFields<N>>()
.expect("will never be `None`");
// Skip formatting the fields if the span had no fields.
if !fields.is_empty() {
//write!(writer, "{{{}}}", fields)?;
}
write!(writer, ": ")?;
}
}
// Write fields on the event
ctx.field_format().format_fields(writer.by_ref(), event)?;
writeln!(writer);
Ok(())
}
}
// thanks: https://stackoverflow.com/a/75546824
pub struct StringVisitor<'a> {
string: &'a mut String,
}
impl<'a> StringVisitor<'a> {
pub(crate) fn new(string: &'a mut String) -> Self {
StringVisitor { string }
}
}
impl<'a> Visit for StringVisitor<'a> {
fn record_debug(&mut self, field: &Field, value: &dyn fmt::Debug) {
if field.name() == "message" {
write!(self.string, "{value:?} ").unwrap();
} else {
write!(self.string, "{} = {:?}; ", field.name(), value).unwrap();
}
}
}

View File

@@ -5,12 +5,12 @@ use std::path::{Path, PathBuf};
use tokio::io::{AsyncReadExt, AsyncWriteExt, Interest}; use tokio::io::{AsyncReadExt, AsyncWriteExt, Interest};
use tokio::net::unix::{OwnedReadHalf, OwnedWriteHalf}; use tokio::net::unix::{OwnedReadHalf, OwnedWriteHalf};
use tokio::net::{UnixListener as TokioUnixListener, UnixStream}; use tokio::net::{UnixListener as TokioUnixListener, UnixStream};
use tracing::{debug, info, warn};
use crate::error::{IntoMizeResult, MizeError, MizeResult}; use crate::error::{IntoMizeResult, MizeError, MizeResult};
use crate::instance::connection::{ConnListener, Connection}; use crate::instance::connection::{ConnListener, Connection};
use crate::instance::{self, Mize}; use crate::instance::{self, Mize};
use crate::proto::{self, MizeMessage}; use crate::proto::{self, MizeMessage};
use crate::prelude::*;
pub struct UnixListener { pub struct UnixListener {
sock_path: PathBuf, sock_path: PathBuf,
@@ -32,7 +32,7 @@ pub fn connect(instance: &mut Mize, store_path: PathBuf) -> MizeResult<()> {
let ns_of_peer_str = instance let ns_of_peer_str = instance
.get(format!( .get(format!(
"inst/con_by_id/{}/peer/0/config/namespace", "inst/con_by_id/{}/peer/self/config/namespace",
conn_id conn_id
))? ))?
.value_string()?; .value_string()?;
@@ -58,7 +58,7 @@ async fn connect_async(mut instance: Mize, store_path: PathBuf) -> MizeResult<u6
let conn_id = instance.new_connection(send_tx)?; let conn_id = instance.new_connection(send_tx)?;
let cloned_instance = instance.clone(); let cloned_instance = instance.clone();
instance.spawn("incomming", move || { instance.spawn_background("incomming", move || {
let result = unix_incomming(unix_read, cloned_instance, conn_id); let result = unix_incomming(unix_read, cloned_instance, conn_id);
// if unix incomming fails, close the connection // if unix incomming fails, close the connection
if let Err(err) = result { if let Err(err) = result {
@@ -68,7 +68,7 @@ async fn connect_async(mut instance: Mize, store_path: PathBuf) -> MizeResult<u6
}); });
let outgoing_cloned_instance = instance.clone(); let outgoing_cloned_instance = instance.clone();
instance.spawn("outgoing", move || { instance.spawn_background("outgoing", move || {
let result = unix_outgoing(unix_write, send_rx, outgoing_cloned_instance, conn_id); let result = unix_outgoing(unix_write, send_rx, outgoing_cloned_instance, conn_id);
// if writing fails, close this connection // if writing fails, close this connection
if let Err(err) = result { if let Err(err) = result {
@@ -91,7 +91,7 @@ async fn unix_listen(listener: UnixListener, mut instance: Mize) -> MizeResult<(
// remove the file at sock_path if it already exists // remove the file at sock_path if it already exists
fs::remove_file(&listener.sock_path); fs::remove_file(&listener.sock_path);
let listener = TokioUnixListener::bind(&listener.sock_path).mize_result_msg(format!( let listener = TokioUnixListener::bind(&listener.sock_path).mres_msg(format!(
"Could not bind to unix socket at '{}'", "Could not bind to unix socket at '{}'",
listener.sock_path.display() listener.sock_path.display()
))?; ))?;
@@ -100,7 +100,7 @@ async fn unix_listen(listener: UnixListener, mut instance: Mize) -> MizeResult<(
let (mut unix_sock, addr) = listener let (mut unix_sock, addr) = listener
.accept() .accept()
.await .await
.mize_result_msg("Error while accepting Unix sock connection")?; .mres_msg("Error while accepting Unix sock connection")?;
info!("new connection"); info!("new connection");
let (send_tx, send_rx) = unbounded::<MizeMessage>(); let (send_tx, send_rx) = unbounded::<MizeMessage>();
@@ -108,7 +108,7 @@ async fn unix_listen(listener: UnixListener, mut instance: Mize) -> MizeResult<(
let conn_id = instance.new_connection(send_tx)?; let conn_id = instance.new_connection(send_tx)?;
let cloned_instance = instance.clone(); let cloned_instance = instance.clone();
instance.spawn("incomming", move || { instance.spawn_background("incomming", move || {
let result = unix_incomming(unix_read, cloned_instance, conn_id); let result = unix_incomming(unix_read, cloned_instance, conn_id);
// if unix incomming fails, close the connection // if unix incomming fails, close the connection
if let Err(err) = result { if let Err(err) = result {
@@ -118,7 +118,7 @@ async fn unix_listen(listener: UnixListener, mut instance: Mize) -> MizeResult<(
}); });
let outgoing_cloned_instance = instance.clone(); let outgoing_cloned_instance = instance.clone();
instance.spawn("outgoing", move || { instance.spawn_background("outgoing", move || {
let result = unix_outgoing(unix_write, send_rx, outgoing_cloned_instance, conn_id); let result = unix_outgoing(unix_write, send_rx, outgoing_cloned_instance, conn_id);
// if writing fails, close this connection // if writing fails, close this connection
if let Err(err) = result { if let Err(err) = result {
@@ -147,7 +147,7 @@ fn unix_outgoing(
Ok(()) Ok(())
} }
fn unix_incomming( pub fn unix_incomming(
mut unix_read: OwnedReadHalf, mut unix_read: OwnedReadHalf,
mut instance: Mize, mut instance: Mize,
conn_id: u64, conn_id: u64,

View File

@@ -0,0 +1,224 @@
// This module is only compiled on Windows
// It provides the same functionality as unix_socket.rs but using Windows named pipes
use ciborium::Value as CborValue;
use flume::{unbounded, Receiver, Sender};
use std::path::PathBuf;
use std::sync::Arc;
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tokio::net::windows::named_pipe::{ClientOptions, ServerOptions};
use tokio::sync::Mutex;
use tracing::{debug, info, warn};
use crate::error::{IntoMizeResult, MizeError, MizeResult};
use crate::instance::connection::{ConnListener, Connection};
use crate::instance::{self, Mize};
use crate::proto::{self, MizeMessage};
pub struct WindowsListener {
pipe_name: String,
}
impl WindowsListener {
pub fn new(store_path: PathBuf) -> MizeResult<WindowsListener> {
// On Windows, we use a named pipe with a specific format
// The format is \.\pipe\<name> for local pipes
// We'll use the store path hash or name to create a unique pipe name
let pipe_name = format!(
"\\.\\pipe\\mize_{}",
store_path.to_string_lossy().replace('\\', "_").replace('/', "_").replace(':', "_")
);
Ok(WindowsListener { pipe_name })
}
}
pub fn connect(instance: &mut Mize, store_path: PathBuf) -> MizeResult<()> {
let conn_id = instance.spawn_async_blocking(
"windows_connection",
connect_async(instance.clone(), store_path),
)?;
let ns_of_peer_str = instance
.get(format!(
"inst/con_by_id/{}/peer/self/config/namespace",
conn_id
))?
.value_string()?;
let ns_of_peer = instance.namespace_from_string(ns_of_peer_str)?;
info!(
"connect_async ... ns_of_peer: {}",
ns_of_peer.clone().as_string()
);
instance.connection_set_namespace(conn_id, ns_of_peer.clone());
instance.set_namespace(ns_of_peer);
Ok(())
}
async fn connect_async(mut instance: Mize, store_path: PathBuf) -> MizeResult<u64> {
let pipe_name = format!(
"\\.\\pipe\\mize_{}",
store_path.to_string_lossy().replace('\\', "_").replace('/', "_").replace(':', "_")
);
// Create the client synchronously
let client = ClientOptions::new().open(&pipe_name)?;
// Wrap the client in an Arc<Mutex> so it can be shared between incoming and outgoing tasks
let shared_client = Arc::new(Mutex::new(client));
let (send_tx, send_rx) = unbounded::<MizeMessage>();
let conn_id = instance.new_connection(send_tx)?;
let cloned_instance = instance.clone();
let incoming_client = shared_client.clone();
instance.spawn_background("incomming", move || {
let result = windows_incomming(incoming_client, cloned_instance, conn_id);
if let Err(err) = result {
warn!("Connection closing because of EOF: {:?}", err);
}
Ok(())
});
let outgoing_cloned_instance = instance.clone();
let outgoing_client = shared_client.clone();
instance.spawn_background("outgoing", move || {
let result = windows_outgoing(outgoing_client, send_rx, outgoing_cloned_instance, conn_id);
if let Err(err) = result {
warn!("Connection closing because of EOF: {:?}", err);
}
Ok(())
});
Ok(conn_id)
}
impl ConnListener for WindowsListener {
fn listen(self, mut instance: Mize) -> MizeResult<()> {
instance.spawn_async("windows listen async", windows_listen(self, instance.clone()));
Ok(())
}
}
async fn windows_listen(listener: WindowsListener, mut instance: Mize) -> MizeResult<()> {
// Create the first server instance
let mut server = ServerOptions::new()
.first_pipe_instance(true)
.create(&listener.pipe_name)?;
loop {
// Wait for a client to connect
server.connect().await.mres_msg("Error while accepting Windows named pipe connection")?;
info!("new connection");
let (send_tx, send_rx) = unbounded::<MizeMessage>();
let conn_id = instance.new_connection(send_tx)?;
// Take ownership of the current server (which is now connected)
let connected_server = server;
// Create a new server for the next connection
server = ServerOptions::new().create(&listener.pipe_name)?;
// Wrap the connected server in an Arc<Mutex> so it can be shared
let shared_server = Arc::new(Mutex::new(connected_server));
let cloned_instance = instance.clone();
let incoming_server = shared_server.clone();
instance.spawn_background("incomming", move || {
let result = windows_incomming(incoming_server, cloned_instance, conn_id);
if let Err(err) = result {
warn!("Connection closing because of EOF: {:?}", err);
}
Ok(())
});
let outgoing_cloned_instance = instance.clone();
let outgoing_server = shared_server.clone();
instance.spawn_background("outgoing", move || {
let result = windows_outgoing(outgoing_server, send_rx, outgoing_cloned_instance, conn_id);
if let Err(err) = result {
warn!("Connection closing because of EOF: {:?}", err);
}
Ok(())
});
}
}
fn windows_outgoing<W>(shared_pipe: Arc<Mutex<W>>, send_rx: Receiver<MizeMessage>, mut instance: Mize, conn_id: u64) -> MizeResult<()>
where
W: AsyncWriteExt + Unpin,
{
for msg in send_rx {
debug!("windows outgoing got msg: {}", msg);
let adapter = MyCiboriumWriter {
inner: &mut *shared_pipe.blocking_lock(),
instance: &mut instance,
};
let value = msg.value();
ciborium::into_writer(&value, adapter)?
}
Ok(())
}
pub fn windows_incomming<R>(shared_pipe: Arc<Mutex<R>>, mut instance: Mize, conn_id: u64) -> MizeResult<()>
where
R: AsyncReadExt + Unpin,
{
loop {
let adapter = MyCiboriumReader {
inner: &mut *shared_pipe.blocking_lock(),
instance: &mut instance,
};
let value: CborValue = ciborium::from_reader(adapter)?;
if let CborValue::Integer(_) = value {
break;
}
let msg = MizeMessage::new(value, conn_id);
debug!("windows incomming got msg: {}", msg);
instance.got_msg(msg);
}
Ok(())
}
struct MyCiboriumWriter<'a, W> {
inner: &'a mut W,
instance: &'a mut Mize,
}
impl<'a, W> ciborium_io::Write for MyCiboriumWriter<'a, W>
where
W: AsyncWriteExt + Unpin,
{
type Error = MizeError;
fn write_all(&mut self, data: &[u8]) -> Result<(), Self::Error> {
let runtime = self.instance.runtime.lock()?;
let _ = runtime.block_on(self.inner.write_all(data))?;
Ok(())
}
fn flush(&mut self) -> Result<(), Self::Error> {
let handle = self.instance.async_get_handle();
let _ = handle.block_on(self.inner.flush());
Ok(())
}
}
struct MyCiboriumReader<'a, R> {
inner: &'a mut R,
instance: &'a mut Mize,
}
impl<'a, R> ciborium_io::Read for MyCiboriumReader<'a, R>
where
R: AsyncReadExt + Unpin,
{
type Error = MizeError;
fn read_exact(&mut self, data: &mut [u8]) -> Result<(), Self::Error> {
let handle = self.instance.async_get_handle();
let _ = handle.block_on(self.inner.read_exact(data))?;
Ok(())
}
}

View File

@@ -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,10 +61,28 @@ 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()
.into_iter() .iter()
.map(|string| CborValue::Text(string.to_owned())) .map(|string| CborValue::Text(string.to_owned()))
.collect(); .collect();
@@ -69,7 +99,7 @@ impl MizeMessage {
pub fn new_get_sub(id: MizeId, conn_id: u64) -> MizeMessage { pub fn new_get_sub(id: MizeId, conn_id: u64) -> MizeMessage {
let id_path = id let id_path = id
.path() .path()
.into_iter() .iter()
.map(|string| CborValue::Text(string.to_owned())) .map(|string| CborValue::Text(string.to_owned()))
.collect(); .collect();
@@ -86,7 +116,7 @@ impl MizeMessage {
pub fn new_sub(id: MizeId, conn_id: u64) -> MizeMessage { pub fn new_sub(id: MizeId, conn_id: u64) -> MizeMessage {
let id_path = id let id_path = id
.path() .path()
.into_iter() .iter()
.map(|string| CborValue::Text(string.to_owned())) .map(|string| CborValue::Text(string.to_owned()))
.collect(); .collect();
@@ -113,7 +143,7 @@ impl MizeMessage {
pub fn new_create_reply(id: MizeId, conn_id: u64) -> MizeMessage { pub fn new_create_reply(id: MizeId, conn_id: u64) -> MizeMessage {
let id_path = id let id_path = id
.path() .path()
.into_iter() .iter()
.map(|string| CborValue::Text(string.to_owned())) .map(|string| CborValue::Text(string.to_owned()))
.collect(); .collect();
@@ -130,7 +160,7 @@ impl MizeMessage {
pub fn new_give(id: MizeId, data: ItemData, conn_id: u64) -> MizeMessage { pub fn new_give(id: MizeId, data: ItemData, conn_id: u64) -> MizeMessage {
let id_path = id let id_path = id
.path() .path()
.into_iter() .iter()
.map(|string| CborValue::Text(string.to_owned())) .map(|string| CborValue::Text(string.to_owned()))
.collect(); .collect();
@@ -148,7 +178,7 @@ impl MizeMessage {
pub fn new_update_request(id: MizeId, data: ItemData, conn_id: u64) -> MizeMessage { pub fn new_update_request(id: MizeId, data: ItemData, conn_id: u64) -> MizeMessage {
let id_path = id let id_path = id
.path() .path()
.into_iter() .iter()
.map(|string| CborValue::Text(string.to_owned())) .map(|string| CborValue::Text(string.to_owned()))
.collect(); .collect();
@@ -166,7 +196,7 @@ impl MizeMessage {
pub fn new_update(id: MizeId, data: ItemData, conn_id: u64) -> MizeMessage { pub fn new_update(id: MizeId, data: ItemData, conn_id: u64) -> MizeMessage {
let id_path = id let id_path = id
.path() .path()
.into_iter() .iter()
.map(|string| CborValue::Text(string.to_owned())) .map(|string| CborValue::Text(string.to_owned()))
.collect(); .collect();
@@ -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();
}); });

View File

@@ -1,8 +1,6 @@
{ {
"workspace": [ "workspace": [
"./packages/ppc/platform/obsidian", "./apps/obsidian-plugin",
"./packages/marts", "./marts"
"./packages/mize" ]
],
"imports": {}
} }

47
deno.lock generated
View File

@@ -1,16 +1,19 @@
{ {
"version": "5", "version": "5",
"specifiers": { "specifiers": {
"jsr:@deno/esbuild-plugin@*": "1.2.1",
"jsr:@deno/esbuild-plugin@^1.2.1": "1.2.1", "jsr:@deno/esbuild-plugin@^1.2.1": "1.2.1",
"jsr:@deno/loader@~0.3.10": "0.3.12", "jsr:@deno/loader@~0.3.10": "0.3.12",
"jsr:@std/internal@^1.0.12": "1.0.12", "jsr:@std/internal@^1.0.12": "1.0.12",
"jsr:@std/path@^1.1.1": "1.1.4", "jsr:@std/path@^1.1.1": "1.1.4",
"npm:@flowershow/remark-wiki-link@^3.4.0": "3.4.0", "npm:@flowershow/remark-wiki-link@^3.4.0": "3.4.0",
"npm:builtin-modules@5": "5.0.0", "npm:builtin-modules@5": "5.0.0",
"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:mdast@3": "3.0.0", "npm:mdast@3": "3.0.0",
"npm:obsidian@^1.12.2": "1.12.2_@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:remark-frontmatter@5": "5.0.0", "npm:remark-frontmatter@5": "5.0.0",
"npm:remark-gfm@^4.0.1": "4.0.1", "npm:remark-gfm@^4.0.1": "4.0.1",
"npm:remark-parse@11": "11.0.0", "npm:remark-parse@11": "11.0.0",
@@ -874,8 +877,8 @@
"ms@2.1.3": { "ms@2.1.3": {
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
}, },
"obsidian@1.12.2_@codemirror+state@6.5.0_@codemirror+view@6.38.6": { "obsidian@1.12.3_@codemirror+state@6.5.0_@codemirror+view@6.38.6": {
"integrity": "sha512-DGAzpt6vo+sMDET/o8Zj26Bj2hbHrFsNUU8TtnzCyerO/OHksMFQnU9QEnPHVsOMstt/WnHXfC56j2r1syObWg==", "integrity": "sha512-HxWqe763dOqzXjnNiHmAJTRERN8KILBSqxDSEqbeSr7W8R8Jxezzbca+nz1LiiqXnMpM8lV2jzAezw3CZ4xNUw==",
"dependencies": [ "dependencies": [
"@codemirror/state", "@codemirror/state",
"@codemirror/view", "@codemirror/view",
@@ -992,25 +995,7 @@
}, },
"workspace": { "workspace": {
"members": { "members": {
"packages/marts": { "apps/obsidian-plugin": {
"packageJson": {
"dependencies": [
"npm:@flowershow/remark-wiki-link@^3.4.0",
"npm:builtin-modules@5",
"npm:esbuild@~0.27.3",
"npm:mdast@3",
"npm:obsidian@^1.12.2",
"npm:remark-frontmatter@5",
"npm:remark-gfm@^4.0.1",
"npm:remark-parse@11",
"npm:remark-stringify@11",
"npm:typescript@^5.9.3",
"npm:unified@^11.0.5",
"npm:unist-util-visit@^5.1.0"
]
}
},
"packages/ppc/platform/obsidian": {
"dependencies": [ "dependencies": [
"jsr:@deno/esbuild-plugin@^1.2.1", "jsr:@deno/esbuild-plugin@^1.2.1",
"npm:@flowershow/remark-wiki-link@^3.4.0", "npm:@flowershow/remark-wiki-link@^3.4.0",
@@ -1026,6 +1011,24 @@
"npm:unified@^11.0.5", "npm:unified@^11.0.5",
"npm:unist-util-visit@^5.1.0" "npm:unist-util-visit@^5.1.0"
] ]
},
"marts": {
"packageJson": {
"dependencies": [
"npm:@flowershow/remark-wiki-link@^3.4.0",
"npm:builtin-modules@5",
"npm:esbuild@~0.27.3",
"npm:mdast@3",
"npm:obsidian@^1.12.3",
"npm:remark-frontmatter@5",
"npm:remark-gfm@^4.0.1",
"npm:remark-parse@11",
"npm:remark-stringify@11",
"npm:typescript@^5.9.3",
"npm:unified@^11.0.5",
"npm:unist-util-visit@^5.1.0"
]
}
} }
} }
} }

0
mize → docs/architecture.md Executable file → Normal file
View File

View File

@@ -0,0 +1,57 @@
## Require clarification and plan approval before making architectural code changes
Before making any code changes which could be considered to influence the architekture of the application, you must follow this two-step process:
### Step 1: Ask Clarifying Questions
- Always ask at least one clarifying question about the user's request
- Understand the full scope and context of what they're asking for
- Clarify any ambiguous requirements or edge cases
- Ask about preferred approaches if multiple solutions exist
- Confirm the expected behavior and user experience
- Ask the clarifying questions by numbering them, so the user can number their replies.
### Step 2: Present Implementation Plan
- After receiving clarification, present a detailed implementation plan
- Break down the work into specific, actionable steps
- Identify which files will be created, modified, or deleted
- Explain the technical approach and any architectural decisions
- Highlight any potential risks, trade-offs, or dependencies
- Estimate the complexity and scope of changes
- **Wait for explicit user approval** before proceeding with any code changes
### What are considered architectural changes
**Architectural** (clarify → plan → wait for approval), for example:
- New parts, or new cross-part `method` / `opt` surfaces
- Wiring one part into another (e.g. UI shell calling `auth.client.login`, soft-notify via `has_part`)
- Changes to core loading / `has_part` / part-system semantics
- Splitting or merging capabilities across part boundaries
**Not architectural** (implement directly when the intent is clear), for example:
- Layout / CSS / copy tweaks in an existing screen
- Adding another display line or button that only uses existing local state or already-exposed methods
- Bugfixes that don’t change the part API (e.g. `document::Style` re-render warnings)
### Approval Requirements
- User must explicitly approve the plan with words like "yes", "approved", "proceed", "go ahead", or similar
- If the user suggests modifications to the plan, incorporate them and seek re-approval
- Do not assume silence or ambiguous responses mean approval
### Exceptions
- This process may be skipped only for trivial changes like fixing obvious typos or formatting
- When in doubt, always follow the full process rather than assuming an exception applies
### Example Workflow
1. User: "Add a login form to the app"
2. Assistant: "I'd like to clarify a few things about the login form: [questions]"
3. User: [provides answers]
4. Assistant: "Based on your requirements, here's my implementation plan: [detailed plan]. Does this approach look good to you?"
5. User: "Yes, that looks good"
6. Assistant: [writes plan to file].
7. Assistant: [proceeds with implementation].

615
docs/part-system.md Normal file
View File

@@ -0,0 +1,615 @@
# Mize Part System (LLM generated guide)
---
## Core Concepts
### The MizePart Trait
All parts implement the `MizePart` trait, which provides:
```rust
pub trait MizePart: MizePartGenerated {
fn name(&self) -> &'static str; // Part identifier
fn get_mize(&mut self) -> &mut Mize; // Access to the Mize instance
fn init(&mut self, mize: &mut Mize) -> MizeResult<()>; // Initialization
fn run(&mut self, mize: &mut Mize) -> MizeResult<()>; // Execution
fn deps(&self) -> &'static [&'static str]; // Dependencies
fn opts(&self, mize: &mut Mize); // Config options
// ... async variants
}
```
### Part Registration
Parts register themselves with the Mize instance using:
- **`mize.register_part(Box::new(MyPart { ... }))`** - For parts with state and methods
- **`mize.add_name_only_part("name")`** - For simple parts that only need to be known by name (and have no state)
### Part Access
Parts can access other parts using:
```rust
// Get a part by name (returns a guard that ensures the part is returned)
let mut part = mize.get_part("part_name")?;
// Get a typed part (for parts with structs)
let mut typed_part = mize.get_part_native::<OtherPart>("other_part")?;
```
**Important**: Users must ensure that dependent parts are enabled before the parts that depend on them. There's no automatic dependency resolution.
---
## Current Implementation (Compiled-in Parts)
Currently, all parts are compiled into the `marts` crate and registered via the `compile_in_parts!` macro in the application's `main.rs`.
### Example: `apps/os/src/main.rs`
```rust
compile_in_parts! {
mize,
"cli": marts::cli,
"auth": marts::part_client_auth,
"spacetime": marts::spacetime,
"ppc.server": marts::ppc::server::part_ppc_server,
"c2vi": marts::part_c2vi,
"sci": marts::sci::part_sci,
"sci.timetrack": marts::sci::timetrack::part_timetrack,
"sci.inbox": marts::sci::inbox::part_sci_inbox,
};
```
Each entry maps a part name to a function that registers the part with the Mize instance.
### How It Works
1. The macro reads the `enable` config option (a list of part names)
2. For each enabled part, it calls the corresponding registration function
3. Each registration function creates the part and calls `mize.register_part()`
---
## Creating a New Part
### Option 1: Simple Name-Only Part
For parts that don't need state or methods:
```rust
// In your part module
pub fn part_my_simple_part(mize: &mut Mize) -> MizeResult<()> {
// Just register the name
mize.add_name_only_part("my_simple_part");
Ok(())
}
```
### Option 2: Part with State and Methods (Using `mize_part` Macro)
For parts that need to store state and expose methods:
```rust
use mize::{mize_part, Mize, MizePart, MizePartCreate, MizeResult};
#[mize_part] // or #[mize_part("custom_name")] for custom name
#[derive(Default)]
pub struct MyPart {
mize: Mize, // Required field
counter: u32, // Your custom state
api_key: String, // More state
}
// Implement MizePartCreate for parts that need custom initialization
impl MizePartCreate for MyPart {
// Optional: override create_generated for custom initialization
}
impl MizePart for MyPart {
// Optional: override name if not using the struct name
fn name(&self) -> &'static str {
"my_part" // Default would be "mypart" from struct name
}
// Optional: define config options this part needs
fn opts(&self, mize: &mut Mize) {
mize.new_opt("my_part.api_key");
mize.new_opt("my_part.counter_initial");
}
// Optional: initialization logic
fn init(&mut self, mize: &mut Mize) -> MizeResult<()> {
self.counter = mize.get_config("my_part.counter_initial")?.value_u32()?;
self.api_key = mize.get_config("my_part.api_key")?.value_string()?;
Ok(())
}
// Optional: define dependencies on other parts
fn deps(&self) -> &'static [&'static str] {
&["auth"] // This part requires "auth" to be loaded first
}
}
// The registration function
pub fn part_my_part(mize: &mut Mize) -> MizeResult<()> {
mize.register_part(Box::new(MyPart {
mize: mize.clone(),
counter: 0,
api_key: String::new(),
}))?;
Ok(())
}
// You can add methods to your part struct
impl MyPart {
pub fn increment(&mut self) -> u32 {
self.counter += 1;
self.counter
}
pub fn get_api_key(&self) -> &str {
&self.api_key
}
}
```
### Using the Part from Another Part
```rust
pub fn part_another_part(mize: &mut Mize) -> MizeResult<()> {
// Get the MyPart instance
let mut my_part = mize.get_part_native::<MyPart>("my_part")?;
// Call its methods
let count = my_part.increment();
let api_key = my_part.get_api_key();
// ... your logic
Ok(())
}
```
---
## The `mize_part` Macro
The `#[mize_part]` procedural macro simplifies part creation by automatically implementing the required traits.
### What It Generates
For a struct like:
```rust
#[mize_part]
#[derive(Default)]
pub struct MyPart {
mize: Mize,
// ... your fields
}
```
The macro generates:
1. **`MizePartGenerated` implementation** - Provides default implementations for the generated trait methods
2. **`MizePartCreateGenerated` implementation** - Provides a default `create_generated` method
3. **`MizePartCreateThisSucks` implementation** - Compatibility trait
4. **Extern "C" function** - Named `make_mize_part_<name>` for dynamic loading
### Custom Part Name
```rust
#[mize_part("custom_name")]
pub struct MyPart {
mize: Mize,
}
```
This will use "custom_name" instead of the derived "mypart".
### Requirements
- The struct **must** have a field named `mize: Mize`
- The struct should derive `Default` or implement it
---
## Configuration
### Defining Config Options
(about to be deprecad, does nothing)
Parts declare what config options they need in their `opts()` method:
```rust
impl MizePart for MyPart {
fn opts(&self, mize: &mut Mize) {
mize.new_opt("my_part.api_url");
mize.new_opt("my_part.timeout");
}
}
```
### Using Config Options
Parts retrieve config using `mize.get_config()`:
```rust
fn init(&mut self, mize: &mut Mize) -> MizeResult<()> {
let api_url = mize.get_config("my_part.api_url")?.value_string()?;
let timeout = mize.get_config("my_part.timeout")?.value_u64()?;
Ok(())
}
```
### User Configuration
Users configure parts via TOML files or environment variables. Example `config.toml`:
```toml
[my_part]
api_url = "https://api.example.com"
timeout = 30
[enable]
# List of parts to enable
my_part = true
auth = true
spacetime = true
```
Or via command line:
```bash
mize --opt my_part.api_url=https://api.example.com run
```
---
## Part Dependencies
### Declaring Dependencies
(about to be deprecad, does nothing)
Parts declare their dependencies in the `deps()` method:
```rust
impl MizePart for MyPart {
fn deps(&self) -> &'static [&'static str] {
&["auth", "spacetime"]
}
}
```
**Note**: This is currently informational only. Users must manually ensure dependencies are enabled.
### Accessing Dependencies
Use `get_part_native()` to access other parts:
```rust
fn run(&mut self, mize: &mut Mize) -> MizeResult<()> {
// Get the auth part
let mut auth = mize.get_part_native::<ClientAuth>("client_auth")?;
// Use it
let token = auth.id_token()?;
// Get spacetime
let mut spacetime = mize.get_part_native::<SpacetimePart>("spacetime")?;
Ok(())
}
```
---
## Future Vision: Separate Crates
The goal is to move each part to its own crate following the naming convention **`mize-part-<name>`**. when creating a part make a new Cargo project like this already.
### Structure of a Part Crate
```
mize-part-discord/
├── Cargo.toml
└── src/
└── lib.rs
```
### Example: `mize-part-discord/Cargo.toml`
```toml
[package]
name = "mize-part-discord"
version = "0.1.0"
edition = "2021"
[lib]
crate-type = ["cdylib", "rlib"]
[dependencies]
mize = { path = "../mize" }
serenity = "0.12"
```
### Example: `mize-part-discord/src/lib.rs`
```rust
use mize::{mize_part, Mize, MizePart, MizePartCreate, MizeResult};
use serenity::Client;
#[mize_part]
#[derive(Default)]
pub struct DiscordPart {
mize: Mize,
client: Option<Client>,
}
impl MizePartCreate for DiscordPart {}
impl MizePart for DiscordPart {
fn name(&self) -> &'static str {
"discord"
}
fn opts(&self, mize: &mut Mize) {
mize.new_opt("discord.token");
}
fn init(&mut self, mize: &mut Mize) -> MizeResult<()> {
let token = mize.get_config("discord.token")?.value_string()?;
// Initialize serenity client...
Ok(())
}
}
// The registration function - THIS IS WHAT MATTERS
pub fn part_discord(mize: &mut Mize) -> MizeResult<()> {
mize.register_part(Box::new(DiscordPart {
mize: mize.clone(),
client: None,
}))?;
Ok(())
}
```
### Using Separate Part Crates
In your application's `Cargo.toml`:
```toml
[dependencies]
mize = { path = "../mize" }
mize-part-discord = { path = "../mize-part-discord" }
mize-part-auth = { path = "../mize-part-auth" }
```
In your application's `main.rs`:
```rust
compile_in_parts! {
mize,
"discord": mize_part_discord::part_discord,
"auth": mize_part_auth::part_auth,
};
```
## Complete Example: Creating a New Part
Let's create a complete example of a new part called `mize-part-greeting`.
### Step 1: Create the Crate
```bash
cargo new --lib mize-part-greeting
cd mize-part-greeting
```
### Step 2: Edit `Cargo.toml`
```toml
[package]
name = "mize-part-greeting"
version = "0.1.0"
edition = "2021"
[lib]
crate-type = ["cdylib", "rlib"]
[dependencies]
mize = { path = "../mize" }
```
### Step 3: Create the Part (`src/lib.rs`)
```rust
use mize::{mize_part, Mize, MizePart, MizePartCreate, MizeResult};
/// A simple greeting part that stores a name and can generate greetings
#[mize_part]
#[derive(Default)]
pub struct GreetingPart {
mize: Mize,
name: String,
}
impl MizePartCreate for GreetingPart {}
impl MizePart for GreetingPart {
fn name(&self) -> &'static str {
"greeting"
}
fn opts(&self, mize: &mut Mize) {
mize.new_opt("greeting.name");
mize.new_opt("greeting.formal");
}
fn init(&mut self, mize: &mut Mize) -> MizeResult<()> {
self.name = mize
.get_config("greeting.name")?
.value_string()
.unwrap_or_else(|_| "World".to_string());
Ok(())
}
}
impl GreetingPart {
/// Generate a greeting message
pub fn greet(&self) -> String {
let formal = self
.mize
.get_config("greeting.formal")
.map(|v| v.value_bool().unwrap_or(false))
.unwrap_or(false);
if formal {
format!("Good day, {}!", self.name)
} else {
format!("Hello, {}!", self.name)
}
}
/// Set the name
pub fn set_name(&mut self, name: String) {
self.name = name;
}
/// Get the current name
pub fn get_name(&self) -> &str {
&self.name
}
}
/// Registration function - this is what the compile_in_parts! macro will call
pub fn part_greeting(mize: &mut Mize) -> MizeResult<()> {
mize.register_part(Box::new(GreetingPart {
mize: mize.clone(),
name: String::new(),
}))?;
Ok(())
}
```
### Step 4: Add to Your Application
In your application's `Cargo.toml`:
```toml
[dependencies]
mize = { path = "../mize" }
mize-part-greeting = { path = "../mize-part-greeting" }
```
In your application's `main.rs`:
```rust
compile_in_parts! {
mize,
"greeting": mize_part_greeting::part_greeting,
};
```
### Step 5: Use the Part
```rust
// In another part or in your CLI
let mut greeting = mize.get_part_native::<GreetingPart>("greeting")?;
println!("{}", greeting.greet());
// Change the name
greeting.set_name("Alice".to_string());
println!("{}", greeting.greet());
```
### Step 6: Configure It
Create a `config.toml`:
```toml
[greeting]
name = "Bob"
formal = true
[enable]
greeting = true
```
Run with:
```bash
mize --config config.toml run
```
---
## Best Practices
1. **Naming**: Use `mize-part-<name>` for crate names and `part_<name>` for registration functions
2. **State**: Store part-specific state in the part struct
3. **Dependencies**: Declare dependencies in `deps()` and document them
4. **Config**: Declare all config options in `opts()`
5. **Error Handling**: Use `MizeResult` and `mize_err!` for errors
6. **Documentation**: Document your part's purpose, config options, and methods
---
## Summary
- Parts are modular units of functionality in Mize
- Each part implements the `MizePart` trait
- Use `#[mize_part]` macro for parts with state and methods
- Register parts with `mize.register_part()` or `mize.add_name_only_part()`
- Parts access config via `mize.get_config()`
- Parts access other parts via `mize.get_part_native::<T>()`
- Users enable parts via the `enable` config option
- Future: Each part will be its own crate named `mize-part-<name>`
---
## Quick Reference
### Common Patterns
**Register a part:**
```rust
mize.register_part(Box::new(MyPart { mize: mize.clone(), ... }))?;
```
**Get a part:**
```rust
let mut part = mize.get_part_native::<MyPart>("my_part")?;
```
**Get config:**
```rust
let value = mize.get_config("my_part.setting")?.value_string()?;
```
**Declare config option:**
```rust
mize.new_opt("my_part.setting");
```
**Define a part struct:**
```rust
#[mize_part]
#[derive(Default)]
pub struct MyPart {
mize: Mize,
// your fields...
}
```
**Registration function:**
```rust
pub fn part_my_part(mize: &mut Mize) -> MizeResult<()> {
mize.register_part(Box::new(MyPart { mize: mize.clone(), ... }))?;
Ok(())
}
```

View File

@@ -0,0 +1,18 @@
# Plan: dist publish + dist.mize.works (approved)
## Config
- `parts.workspaces`: list of dirs; each subdir is a part to build/upload
- `parts.dist.cd`: bool; when true artifact uses `-cd` instead of `-{version}`
## Publish (`mize.dev`)
- After build: pack each part `.so` under `lib/`, special part `mize` gets `libmize.so` + `bin/mize`
- Hash via `get_part_hash` with modName, system (target), mize_version
- Filename: `{hash}-{id}-{version}.tar.gz` or `{hash}-{id}-cd.tar.gz`
- rsync to `ocib:host/mize-dist/`
## fetch_part
- Base URL `https://dist.mize.works/`
- Same naming; ensure mize_version in selector
## ocib
- `~/host/docker-compose.yaml`: Traefik + kugland/darkhttpd serving `mize-dist` for `dist.mize.works`

View File

@@ -0,0 +1,14 @@
# load_part_at instances (priv.spacetime / ppc.spacetime)
Approved 2026-11-04.
## Goal
Instantiate the same `spacetime` `.so` under different part ids (`priv.spacetime`, `ppc.spacetime`), each with its own config namespace. Dependents use `Spacetime::get("priv.spacetime")` etc.
## Changes
1. Platform `load_part(part_name, path, at_id)` via `get_global_mize()`; save/restore `cur_eval_part`.
2. Rename `Mize::load_part_at` → `load_part_from_path`.
3. `config::load_part_at(at_id, part_name)` — no-op in generate mode.
4. Codegen: special SpacetimeDB client gen when `gen` contains `"spacetime"`, module at `{cwd}/spacetime` if present.
5. `sci`: `gen += spacetime`, `load_part_at("priv.spacetime", "spacetime")`.
6. New `mize/parts/ppc`: loads `ppc.spacetime`.

View File

@@ -72,11 +72,11 @@
}, },
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1770115704, "lastModified": 1782467914,
"narHash": "sha256-KHFT9UWOF2yRPlAnSXQJh6uVcgNcWlFqqiAZ7OVlHNc=", "narHash": "sha256-pGvFkM8N0xEkIIXDe5YYfbEAvHrk4IxBrjB/x8OomhE=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "e6eae2ee2110f3d31110d5c222cd395303343b08", "rev": "e73de5be04e0eff4190a1432b946d469c794e7b4",
"type": "github" "type": "github"
}, },
"original": { "original": {

382
flake.nix Normal file
View File

@@ -0,0 +1,382 @@
{
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
fenix = {
url = "github:nix-community/fenix";
inputs.nixpkgs.follows = "nixpkgs";
};
crane = {
url = "github:ipetkov/crane";
};
rust-overlay = {
url = "github:oxalica/rust-overlay";
inputs.nixpkgs.follows = "nixpkgs";
};
mize_modules = {
url = "github:c2vi/mize-modules";
flake = false;
};
flake-utils.url = "github:numtide/flake-utils";
};
outputs = { self, flake-utils, nixpkgs, fenix, crane, mize_modules, rust-overlay, ... }@inputs: flake-utils.lib.eachDefaultSystem (system:
############################## LET BINDINGS ##############################
let
pkgs = nixpkgs.legacyPackages.${system};
osToolchain = fenix.packages.${system}.latest.toolchain;
osCrane = (crane.mkLib pkgs).overrideToolchain osToolchain;
# mize build time config
mizeBuildConfig = {
namespace = "mize.buildtime.ns";
module_url = "parts.mize.works";
};
in {
############################## PACKAGES ##############################
packages = rec {
fileSetForCrate =
crate:
pkgs.lib.fileset.toSource {
root = ./.;
fileset = pkgs.lib.fileset.unions [
./Cargo.toml
./Cargo.lock
(osCrane.fileset.commonCargoSources ./packages/marts)
(osCrane.fileset.commonCargoSources ./packages/mize)
(osCrane.fileset.commonCargoSources ./packages/ppc/spacetimedb)
(osCrane.fileset.commonCargoSources ./packages/mme)
(osCrane.fileset.commonCargoSources ./packages/vic)
(osCrane.fileset.commonCargoSources ./packages/ac_mize_macros)
(osCrane.fileset.commonCargoSources crate)
];
};
ppc = osCrane.buildPackage {
src = fileSetForCrate ./packages/ppc;
cargoExtraArgs = "-p ppc";
nativeBuildInputs = [
pkgs.pkg-config
pkgs.gobject-introspection
pkgs.gobject-introspection.dev
];
buildInputs = with pkgs; [
git # needed to build spacetimedb as it needs to get it's git hash for setting it's version
at-spi2-atk
atkmm
cairo
gdk-pixbuf
glib
gtk3
harfbuzz
librsvg
libsoup_3
pango
webkitgtk_4_1
openssl
wasm-bindgen-cli
lld_20
xdotool
];
LD_LIBRARY_PATH = with pkgs; pkgs.lib.makeLibraryPath [
xdotool
webkitgtk_4_1
pango
openssl
at-spi2-atk
atkmm
cairo
gdk-pixbuf
glib
gtk3
harfbuzz
librsvg
libsoup_3
];
};
mize = osCrane.buildPackage {
src = fileSetForCrate ./packages/mize;
cargoExtraArgs = "-p mize";
nativeBuildInputs = [
pkgs.pkg-config
pkgs.gobject-introspection
pkgs.gobject-introspection.dev
];
buildInputs = with pkgs; [
git # needed to build spacetimedb as it needs to get it's git hash for setting it's version
at-spi2-atk
atkmm
cairo
gdk-pixbuf
glib
gtk3
harfbuzz
librsvg
libsoup_3
pango
webkitgtk_4_1
openssl
wasm-bindgen-cli
lld_20
xdotool
];
LD_LIBRARY_PATH = with pkgs; pkgs.lib.makeLibraryPath [
xdotool
webkitgtk_4_1
pango
openssl
at-spi2-atk
atkmm
cairo
gdk-pixbuf
glib
gtk3
harfbuzz
librsvg
libsoup_3
];
};
};
############################## DEV SHELLS ##############################
devShells = {
# shell without spacetime and other dev commands. just all needed to build mize or ppc
buildShell = pkgs.mkShell {
MIZE_BUILD_CONFIG = builtins.toJSON mizeBuildConfig;
shellHook = ''
mize_project_root=$(${pkgs.lib.getExe pkgs.git} rev-parse --show-toplevel)
export MIZE_CONFIG_FILES=$mize_project_root/mize_config.toml
'';
nativeBuildInputs = [
pkgs.wasm-pack
pkgs.pkg-config
pkgs.gobject-introspection
pkgs.gobject-introspection.dev
pkgs.deno
# thanks to: https://github.com/NixOS/nixpkgs/pull/496279/changes
(pkgs.buildWasmBindgenCli rec {
src = pkgs.fetchCrate {
pname = "wasm-bindgen-cli";
version = "0.2.114";
hash = "sha256-xrCym+rFY6EUQFWyWl6OPA+LtftpUAE5pIaElAIVqW0=";
};
cargoDeps = pkgs.rustPlatform.fetchCargoVendor {
inherit src;
inherit (src) pname version;
hash = "sha256-Z8+dUXPQq7S+Q7DWNr2Y9d8GMuEdSnq00quUR0wDNPM=";
};
})
(fenix.packages.${system}.combine [
fenix.packages.${system}.latest.toolchain
fenix.packages.${system}.targets.wasm32-unknown-unknown.latest.toolchain
])
];
buildInputs = with pkgs; [
at-spi2-atk
atkmm
cairo
gdk-pixbuf
glib
gtk3
harfbuzz
librsvg
libsoup_3
pango
webkitgtk_4_1
openssl
wasm-bindgen-cli
lld_20
xdotool
];
LD_LIBRARY_PATH = with pkgs; pkgs.lib.makeLibraryPath [
xdotool
webkitgtk_4_1
pango
openssl
at-spi2-atk
atkmm
cairo
gdk-pixbuf
glib
gtk3
harfbuzz
librsvg
libsoup_3
];
};
default = pkgs.mkShell rec {
MIZE_BUILD_CONFIG = builtins.toJSON mizeBuildConfig;
shellHook = ''
mize_project_root=$(${pkgs.lib.getExe pkgs.git} rev-parse --show-toplevel)
'';
#_shell_type = "rust";
nativeBuildInputs = [
pkgs.wasm-pack
pkgs.dioxus-cli
pkgs.spacetimedb
pkgs.deno
pkgs.pkg-config
pkgs.gobject-introspection
pkgs.gobject-introspection.dev
pkgs.systemd
pkgs.rsync
pkgs.openssh
pkgs.cmake
# to build ../grid
pkgs.rustPlatform.bindgenHook
pkgs.pipewire.dev
pkgs.alsa-lib.dev
# thanks to: https://github.com/NixOS/nixpkgs/pull/496279/changes
(pkgs.buildWasmBindgenCli rec {
src = pkgs.fetchCrate {
pname = "wasm-bindgen-cli";
version = "0.2.114";
hash = "sha256-xrCym+rFY6EUQFWyWl6OPA+LtftpUAE5pIaElAIVqW0=";
};
cargoDeps = pkgs.rustPlatform.fetchCargoVendor {
inherit src;
inherit (src) pname version;
hash = "sha256-Z8+dUXPQq7S+Q7DWNr2Y9d8GMuEdSnq00quUR0wDNPM=";
};
})
(fenix.packages.${system}.combine [
fenix.packages.${system}.latest.toolchain
fenix.packages.${system}.targets.wasm32-unknown-unknown.latest.toolchain
])
];
buildInputs = with pkgs; [
at-spi2-atk
atkmm
cairo
gdk-pixbuf
glib
gtk3
harfbuzz
librsvg
libsoup_3
pango
webkitgtk_4_1
openssl
wasm-bindgen-cli
lld_20
xdotool
bzip2
];
LD_LIBRARY_PATH = with pkgs; pkgs.lib.makeLibraryPath [
xdotool
webkitgtk_4_1
pango
openssl
at-spi2-atk
atkmm
cairo
gdk-pixbuf
glib
gtk3
harfbuzz
librsvg
libsoup_3
# to build ../grid
libxkbcommon
alsa-lib
libgcc.lib
libxcb
pipewire
wayland
# for rust-analyzer to work for zed
bzip2
];
LIBRARY_PATH = LD_LIBRARY_PATH;
};
};
}) // rec {
############################## SOME GLOBAL OUTPUTS ##############################
inherit inputs self;
############################## NIXOS MODULES ##############################
nixosModules.default = { system, lib, config, pkgs, ... }: {
options.mize = {
enable = lib.mkEnableOption "enable the mize damon";
debugBinary = lib.mkOption {
type = lib.types.nullOr lib.types.str;
default = null;
example = "/home/user/here/mize";
description = "If set to NOT null, the mize daemon service will use this path instead of $${self.packages.system.mize}/bin/mize''";
};
debugConfigFile = lib.mkOption {
type = lib.types.nullOr lib.types.str;
default = null;
example = "/home/user/here/mize.toml";
description = "If set to NOT null, the mize daemon service will use the config file at this path instead of one written to the nix store";
};
enableConfigFile = lib.mkOption {
type = lib.types.bool;
default = true;
description = "allows to disable setting MIZE_CONFIG_FILES env var for the mize daemon service";
};
config = lib.mkOption {
type = lib.types.attrsOf lib.types.anything;
default = {};
example = {
dat_dir = "home/user/my_mize_data";
spacetime.url = "spacetime.example.com";
};
description = "mize config for the daemon";
};
};
config = lib.mkIf config.mize.enable {
systemd.user.services.mize = {
description = "the mize daemon";
wantedBy = [ "default.target" ];
environment = lib.mkIf config.mize.enableConfigFile {
MIZE_CONFIG_FILES = let fileInStore = (pkgs.formats.toml {}).generate "mize-daemon-config.toml" config.mize.config; in if config.mize.debugConfigFile == null then "${fileInStore}" else "${config.mize.debugConfigFile}";
};
serviceConfig = {
Restart = "on-failure";
ExecStart = if config.mize.debugBinary == null then "${self.packages.${system}.mize}/bin/mize run" else "${config.mize.debugBinary} run";
#After = [ "graphical-session.target" ];
PartOf = [ "graphical-session.target" ]; # this should mean DISPLAY and WAYLAND_DISPLAY are set in the service env...
};
};
};
};
# for what we do the nixos and home-manager api is identical
homeModules.default = nixosModules.default;
};
}

View File

@@ -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"

554
macros/src/lib.rs Normal file
View File

@@ -0,0 +1,554 @@
use proc_macro::TokenStream;
use quote::quote;
use syn::{self, parse::Parse, parse::ParseStream, Ident, LitStr, Token, Type, TypePath};
use syn::punctuated::Punctuated;
use syn::token;
//////////////////////////////////// include_native! macro ///////////////////////////////////////
/// A procedural macro to register methods and associated functions of a foreign type with mize.
///
/// Usage 1: Simplified syntax with no-argument methods
/// ```ignore
/// include_native!("Command", clap::Command, [get_name, is_empty]);
/// ```
///
/// Usage 2: With argument specifications
/// ```ignore
/// include_native!("Command", clap::Command, [
/// arg(arg: clap::Arg),
/// subcommand(cmd: clap::Command)
/// ]);
/// ```
///
/// Usage 3: Block syntax separating associated functions and methods
/// ```ignore
/// include_native!("Command", clap::Command, {
/// associated: [new(name: String)],
/// methods: [arg(arg: clap::Arg), subcommand(cmd: clap::Command)]
/// });
/// ```
/// A function specification: name or name(arg1: Type1, arg2: Type2, ...)
#[derive(Debug, Clone)]
struct FuncSpec {
name: Ident,
args: Vec<(Ident, Type)>,
}
impl Parse for FuncSpec {
fn parse(input: ParseStream) -> syn::Result<Self> {
let name: Ident = input.parse()?;
let mut args = Vec::new();
// Check for parentheses with arguments
if input.peek(syn::token::Paren) {
let content;
syn::parenthesized!(content in input);
// Parse comma-separated arg: Type pairs
while !content.is_empty() {
let arg_name: Ident = content.parse()?;
content.parse::<Token![:]>()?;
let arg_type: Type = content.parse()?;
args.push((arg_name, arg_type));
if !content.is_empty() {
content.parse::<Token![,]>()?;
}
}
}
Ok(FuncSpec { name, args })
}
}
/// Helper struct to parse a list of function specifications
struct FuncSpecList(Vec<FuncSpec>);
impl Parse for FuncSpecList {
fn parse(input: ParseStream) -> syn::Result<Self> {
let punctuated = Punctuated::<FuncSpec, Token![,]>::parse_terminated(input)?;
Ok(FuncSpecList(punctuated.into_iter().collect()))
}
}
/// Input for the simplified syntax: include_native!("name", Type, [method1, method2, ...])
struct IncludeNativeSimple {
name: LitStr,
type_path: TypePath,
methods: Vec<FuncSpec>,
}
impl Parse for IncludeNativeSimple {
fn parse(input: ParseStream) -> syn::Result<Self> {
let name = input.parse()?;
input.parse::<Token![,]>()?;
let type_path = input.parse()?;
input.parse::<Token![,]>()?;
let content;
syn::bracketed!(content in input);
let methods = content.parse::<FuncSpecList>()?.0;
Ok(IncludeNativeSimple { name, type_path, methods })
}
}
/// Struct for parsing the block syntax with associated and methods
struct IncludeNativeBlock {
name: LitStr,
type_path: TypePath,
associated: Vec<FuncSpec>,
methods: Vec<FuncSpec>,
}
impl Parse for IncludeNativeBlock {
fn parse(input: ParseStream) -> syn::Result<Self> {
let name = input.parse()?;
input.parse::<Token![,]>()?;
let type_path = input.parse()?;
input.parse::<Token![,]>()?;
let content;
syn::braced!(content in input);
let mut associated = Vec::new();
let mut methods = Vec::new();
// Parse the content looking for associated: [...] and methods: [...]
// We'll collect all tokens first and then parse
let tokens: Vec<proc_macro2::TokenTree> = content.parse::<proc_macro2::TokenStream>()?.into_iter().collect();
let mut i = 0;
while i < tokens.len() {
if let proc_macro2::TokenTree::Ident(id) = &tokens[i] {
let id_str = id.to_string();
if id_str == "associated" {
// Find colon
let mut j = i + 1;
while j < tokens.len() {
if let proc_macro2::TokenTree::Punct(p) = &tokens[j] {
if p.as_char() == ':' {
break;
}
}
j += 1;
}
j += 1; // Skip colon
// Find bracket group
while j < tokens.len() {
if let proc_macro2::TokenTree::Group(g) = &tokens[j] {
if g.delimiter() == proc_macro2::Delimiter::Bracket {
associated = syn::parse2::<FuncSpecList>(g.stream())
.map(|l| l.0)
.unwrap_or_default();
break;
}
}
j += 1;
}
i = j + 1;
} else if id_str == "methods" {
// Find colon
let mut j = i + 1;
while j < tokens.len() {
if let proc_macro2::TokenTree::Punct(p) = &tokens[j] {
if p.as_char() == ':' {
break;
}
}
j += 1;
}
j += 1; // Skip colon
// Find bracket group
while j < tokens.len() {
if let proc_macro2::TokenTree::Group(g) = &tokens[j] {
if g.delimiter() == proc_macro2::Delimiter::Bracket {
methods = syn::parse2::<FuncSpecList>(g.stream())
.map(|l| l.0)
.unwrap_or_default();
break;
}
}
j += 1;
}
i = j + 1;
} else {
i += 1;
}
} else {
i += 1;
}
}
Ok(IncludeNativeBlock { name, type_path, associated, methods })
}
}
#[proc_macro]
pub fn include_native(input: TokenStream) -> TokenStream {
// Try to parse as simplified syntax: include_native!("name", Type, [m1, m2, ...])
if let Ok(simple) = syn::parse::<IncludeNativeSimple>(input.clone()) {
return expand_simple_include_native(simple);
}
// Try to parse as block syntax: include_native!("name", Type, { associated: [...], methods: [...] })
if let Ok(block) = syn::parse::<IncludeNativeBlock>(input) {
return expand_block_include_native(block);
}
// If both fail, return a compile error
syn::Error::new(
proc_macro2::Span::call_site(),
"Failed to parse include_native! input. Expected:\n \
include_native!(\"name\", Type, [method1, method2(arg: Type), ...])\n or\n \
include_native!(\"name\", Type, { associated: [func1(arg: Type)], methods: [method1] })",
)
.to_compile_error()
.into()
}
/// Generate the argument extraction code for a function spec
fn generate_arg_extraction(args: &[(Ident, Type)]) -> Vec<proc_macro2::TokenStream> {
args.iter().enumerate().map(|(i, (arg_name, arg_type))| {
quote! {
let #arg_name = args.get_path(#i)?.native_clone::<#arg_type>()?;
}
}).collect()
}
/// Generate the argument passing code for a function call
fn generate_arg_passing(args: &[(Ident, Type)]) -> Vec<proc_macro2::TokenStream> {
args.iter().map(|(arg_name, _)| {
quote! { #arg_name }
}).collect()
}
/// Last path segment of a Type, for signature strings (e.g. `clap::Command` -> `Command`).
fn type_to_sig_name(ty: &Type) -> String {
match ty {
Type::Path(p) => p
.path
.segments
.last()
.map(|s| s.ident.to_string())
.unwrap_or_else(|| quote!(#ty).to_string()),
_ => quote!(#ty).to_string().replace(' ', ""),
}
}
/// Build a signature string like `"String, Command"` from FuncSpec args.
fn synthesize_signature(args: &[(Ident, Type)]) -> String {
args.iter()
.map(|(_, ty)| type_to_sig_name(ty))
.collect::<Vec<_>>()
.join(", ")
}
fn expand_simple_include_native(simple: IncludeNativeSimple) -> TokenStream {
let name = simple.name;
let type_path = simple.type_path;
let methods = simple.methods;
let type_name_str = quote!(#type_path).to_string().replace(' ', "");
let method_names: Vec<String> = methods.iter().map(|m| m.name.to_string()).collect();
let method_sigs: Vec<String> = methods.iter().map(|m| synthesize_signature(&m.args)).collect();
let method_calls = methods.iter().map(|method| {
let method_name = method.name.to_string();
let method_ident = &method.name;
let signature = synthesize_signature(&method.args);
let arg_extractions = generate_arg_extraction(&method.args);
let arg_passings = generate_arg_passing(&method.args);
quote! {
.method(#method_name, #signature, |args| {
#(#arg_extractions)*
let this = mize::prelude::get_global_mize()?
.get_config(#name)?
.native_mut::<#type_path>()?;
let result = this.#method_ident(#(#arg_passings),*);
Ok(mize::item::ItemData::from_native(result)?)
})
}
});
let expanded = quote! {
{
if let Ok(mize) = mize::prelude::get_global_mize() {
if mize.is_generate_mode().unwrap_or(false) {
let _ = mize.record_native(
#name,
#type_name_str,
Vec::new(),
vec![#(#method_names.to_string()),*],
Vec::new(),
vec![#(#method_sigs.to_string()),*],
);
}
}
mize::config::opt(#name)
#(#method_calls)*
}
};
TokenStream::from(expanded)
}
fn expand_block_include_native(block: IncludeNativeBlock) -> TokenStream {
let name = block.name;
let type_path = block.type_path;
let associated = block.associated;
let methods = block.methods;
let type_name_str = quote!(#type_path).to_string().replace(' ', "");
let associated_names: Vec<String> = associated.iter().map(|m| m.name.to_string()).collect();
let method_names: Vec<String> = methods.iter().map(|m| m.name.to_string()).collect();
let associated_sigs: Vec<String> = associated.iter().map(|m| synthesize_signature(&m.args)).collect();
let method_sigs: Vec<String> = methods.iter().map(|m| synthesize_signature(&m.args)).collect();
let method_calls = methods.iter().map(|method| {
let method_name = method.name.to_string();
let method_ident = &method.name;
let signature = synthesize_signature(&method.args);
let arg_extractions = generate_arg_extraction(&method.args);
let arg_passings = generate_arg_passing(&method.args);
quote! {
.method(#method_name, #signature, |args| {
#(#arg_extractions)*
let this = mize::prelude::get_global_mize()?
.get_config(#name)?
.native_mut::<#type_path>()?;
let result = this.#method_ident(#(#arg_passings),*);
Ok(mize::item::ItemData::from_native(result)?)
})
}
});
let associated_calls = associated.iter().map(|func| {
let func_name = func.name.to_string();
let func_ident = &func.name;
let signature = synthesize_signature(&func.args);
let arg_extractions = generate_arg_extraction(&func.args);
let arg_passings = generate_arg_passing(&func.args);
quote! {
.method(#func_name, #signature, |args| {
#(#arg_extractions)*
let result = <#type_path>::#func_ident(#(#arg_passings),*);
Ok(mize::item::ItemData::from_native(result)?)
})
}
});
let expanded = quote! {
{
if let Ok(mize) = mize::prelude::get_global_mize() {
if mize.is_generate_mode().unwrap_or(false) {
let _ = mize.record_native(
#name,
#type_name_str,
vec![#(#associated_names.to_string()),*],
vec![#(#method_names.to_string()),*],
vec![#(#associated_sigs.to_string()),*],
vec![#(#method_sigs.to_string()),*],
);
}
}
mize::config::opt(#name)
#(#associated_calls)*
#(#method_calls)*
}
};
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::Lit;
// --- 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),
#[allow(unused)]
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)
}

5
md
View File

@@ -1,5 +0,0 @@
#!/bin/sh
if [[ "$1" == "dob" ]]; then
fi

View File

@@ -1,85 +0,0 @@
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 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()
}
}
}
/*
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)
}

View File

@@ -1,21 +0,0 @@
[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 }
deno_core = { version = "0.385.0", optional = true }
flume = "0.12.0"
futures = "0.3.32"
mize = { path = "../mize/", default-features = false }
tokio = "1.49.0"
[features]
default = ["target-os"]
target-os = [ "clap", "mize/target-os", "deno_core" ]
target-wasm = [ "mize/target-wasm" ]
[lib]
path = "lib.rs"

View File

@@ -1,170 +0,0 @@
export function c2vi_obsidian_canvas_patch(mize: any) {
const plugin = mize.get_part_native("obsidian");
window.pc = patchCanvas;
window.upc = () => {
unpatchCanvas(plugin, plugin.app.workspace.activeLeaf);
};
console.log("hereeeeeeeeeeeeeeeee");
// canvas patching
plugin.registerEvent(
plugin.app.workspace.on("file-open", (file: any) => {
if (file?.extension === "canvas") {
maybePathCanvas(plugin, plugin.app.workspace.activeLeaf);
}
}),
);
plugin.registerEvent(
plugin.app.workspace.on("active-leaf-change", (file: any) => {
if (file?.extension === "canvas") {
maybePathCanvas(plugin, plugin.app.workspace.activeLeaf);
}
}),
);
}
const maybePathCanvas = (plugin: PPCObsidianPlugin, leaf: any | null) => {
console.log("maybePathCanvas called");
const file = plugin.app.workspace.getActiveFile();
if (file?.extension !== "canvas") return;
console.log("patching canvas.....");
const canvas = plugin.app.workspace.activeLeaf.view.canvas;
if ("isPatched" in canvas) return;
patchCanvas(plugin, leaf, canvas);
};
function patchCanvas(plugin: PPCObsidianPlugin, leaf: any, canvas: any) {
canvas.isPatched = true;
const canvasEl = canvas.canvasEl;
const canvasWrapperEl = canvasEl.parentElement;
const overlay = createDiv();
overlay.setAttr("class", "c2vi-canvas-overlay");
const canvas_card_menue = canvasWrapperEl.childNodes[1];
canvasWrapperEl.insertBefore(overlay, canvas_card_menue);
Object.assign(overlay.style, {
position: "absolute",
inset: "0",
//zIndex: "1337",
});
overlay.addEventListener("contextmenu", (e) => {
if (e.button === 2) {
e.preventDefault();
e.stopPropagation();
e.stopImmediatePropagation();
}
});
overlay.addEventListener("mouseout", (e) => {
const menu = document.getElementsByClassName("menu")[0];
if (!menu) return;
menu.addEventListener("pointerup", (e) => {
console.log("pointerup on menu", e);
e.target.click();
/*
menu.dispatchEvent(
new MouseEvent("mousedown", {
bubbles: true,
cancelable: true,
composed: true,
clientX: e.clientX,
clientY: e.clientY,
button: e.button,
buttons: e.buttons,
view: window,
}),
);
*/
});
});
setupEventPropagation("pointerdown", PointerEvent, overlay, canvasWrapperEl);
setupEventPropagation("pointerup", PointerEvent, overlay, canvasWrapperEl);
setupEventPropagation("mousedown", MouseEvent, overlay, canvasWrapperEl);
setupEventPropagation("mouseup", MouseEvent, overlay, canvasWrapperEl);
setupEventPropagation("click", MouseEvent, overlay, canvasWrapperEl);
setupEventPropagation("dragstart", DragEvent, overlay, canvasWrapperEl);
setupEventPropagation("dragstop", DragEvent, overlay, canvasWrapperEl);
window.a = canvasWrapperEl;
window.b = overlay;
const mouseEvents = [
"mousedown",
"mouseup",
"click",
"dblclick",
//"mousemove",
"mouseover",
"mouseout",
"wheel",
];
mouseEvents.forEach((eventType) => {
canvasWrapperEl.addEventListener(
eventType,
(event) => {
console.log(`Mouse Event Triggered: ${event.type}`, event);
},
{ passive: true },
);
});
}
function unpatchCanvas(plugin: PPCObsidianPlugin, leaf: any) {
const canvas = plugin.app.workspace.activeLeaf.view.canvas;
if (!canvas.isPatched) return;
canvas.isPatched = false;
const canvasEl = canvas.canvasEl;
const canvasWrapperEl = canvasEl.parentElement;
const targets = canvasEl.querySelectorAll(":scope > .c2vi-canvas-overlay");
targets.forEach((child) => child.remove());
}
function setupEventPropagation(type: string, evClass, overlay: any, target) {
overlay.addEventListener(type, (originalEvent) => {
originalEvent.stopPropagation();
originalEvent.stopImmediatePropagation();
originalEvent.preventDefault();
const newEvent = new evClass(type, originalEvent);
if (type == "pointerdown" && originalEvent.ctrlKey == true) {
dispatchContextMenue(
target,
originalEvent.clientX,
originalEvent.clientY,
);
return;
}
if (type === "pointerdown") {
window.downEv = newEvent;
}
if (type === "pointerup") {
window.upEv = newEvent;
}
newEvent.isTrusted = true;
console.log("original Event of", type, originalEvent);
console.log("new Event of", type, newEvent);
target.dispatchEvent(newEvent);
});
}
function dispatchContextMenue(target, x, y) {
const newEvent = new PointerEvent("contextmenu", {
bubbles: true,
cancelable: true,
composed: true,
view: window,
clientX: x,
clientY: y,
pointerType: "mouse",
button: 2,
buttons: 2,
pointerId: 1,
isPrimary: true,
});
target.dispatchEvent(newEvent);
}

View File

@@ -1,60 +0,0 @@
use clap::{ArgMatches, Command};
use std::collections::HashMap;
use mize::{mize_part, Mize, MizePart, MizeResult};
#[mize_part("cli")]
#[derive(Default)]
pub struct CliPart {
mize: Mize,
cmd: Option<Command>,
actions: HashMap<String, fn(&ArgMatches) -> MizeResult<()>>,
}
pub fn cli(mize: &mut Mize) -> MizeResult<()> {
let cli_part = CliPart {
mize: mize.clone(),
cmd: Some(Command::new("marts-cli")),
actions: HashMap::new(),
};
mize.register_part(Box::new(cli_part))
}
impl MizePart for CliPart {
fn init(&mut self, _mize: &mut Mize) -> MizeResult<()> {
Ok(())
}
fn run(&mut self, _mize: &mut Mize) -> MizeResult<()> {
let matches = self.cmd.take().unwrap().get_matches();
let sub_cmd = matches.subcommand_name().unwrap();
let action = self.actions.get(sub_cmd).unwrap();
action(matches.subcommand_matches(sub_cmd).unwrap())?;
Ok(())
}
fn opts(&self, mize: &mut Mize) {
mize.new_opt("cli.name");
}
}
impl CliPart {
pub fn subcommand(
&mut self,
subcmd: Command,
action: fn(sub_matches: &ArgMatches) -> MizeResult<()>,
) -> &mut Command {
let name = subcmd.get_name().to_string();
let cmd = self.cmd.take().unwrap().subcommand(subcmd);
self.actions.insert(name, action);
self.cmd = Some(cmd);
self.cmd.as_mut().unwrap()
}
pub fn with_cmd(
&mut self,
func: fn(mize: Mize, cmd: Command) -> MizeResult<Command>,
) -> MizeResult<()> {
let cmd = func(self.mize.clone(), self.cmd.take().unwrap())?;
self.cmd = Some(cmd);
Ok(())
}
}

View File

@@ -1,11 +0,0 @@
{
"name": "@ppc/marts",
"imports": {},
"exports": {
".": "./index.ts",
"stub_deno": "./js/stub_deno.ts"
},
"tasks": {
"build": "deno bundle -o ./deno_dist/habitica.js ./habitica.ts"
}
}

View File

@@ -1,75 +0,0 @@
// habitica.ts
var opts = {
"habitica.api_url": {
env_var_name: "PPC_HABITICA_API_URL",
default_val: "https://habitica.com/api/v3"
},
"habitica.user_id": {
env_var_name: "PPC_HABITICA_USER_ID"
},
"habitica.client_name": {
default_val: "3544a0b8-d71a-46e0-9bb1-6ddbb2abcddb-PPC-Software"
},
"habitica.api_token": {
env_var_name: "PPC_HABITICA_API_TOKEN"
}
};
async function habitica(mize) {
console.log("hiiiiiiiiiiiiiiiiiiii");
mize.add_opts(opts);
mize.add_part(new Habitica(mize));
}
async function handleRateLimit(response) {
const limit = response.headers.get("X-RateLimit-Limit") || "NONE";
const remaining = parseInt(response.headers.get("X-RateLimit-Remaining") || "10", 10);
const reset = response.headers.get("X-RateLimit-Reset");
console.log(`RateLimit: ${limit} | Remaining: ${remaining} | Reset: ${reset}`);
if (remaining < 2 && reset) {
const resetDate = new Date(reset);
const now = /* @__PURE__ */ new Date();
const waitMs = resetDate.getTime() - now.getTime() + 1e3;
if (waitMs > 0) {
console.log(`Waiting ${Math.round(waitMs / 1e3)} secs for next rate limit window...`);
await new Promise((resolve) => setTimeout(resolve, waitMs));
}
}
}
var Habitica = class {
mize;
constructor(mize) {
this.mize = mize;
}
async get_tasks(type = "todos") {
return await this.api_request("GET", `tasks/user?type=${type}`);
}
async delete_task(id) {
await this.api_request("DELETE", `tasks/${id}`);
}
async api_request(method, path, extraHeaders = {}, data = {}) {
const mize = this.mize;
if (!path.startsWith("/")) {
path = "/" + path;
}
const headers = Object.assign({
"Content-Type": "application/json",
"x-api-user": mize.get_config("habitica.user_id"),
"x-api-key": mize.get_config("habitica.api_token"),
"x-client": mize.get_config("habitica.client_name")
}, extraHeaders);
const response = await fetch(mize.get_config("habitica.api_url") + path, {
method,
headers,
body: JSON.stringify(data)
});
await handleRateLimit(response);
if (!response.ok) {
console.log("request_url:", mize.get_config("habitica.api_url") + path);
throw new Error(`Habitica API error: ${response.status} ${response.statusText}`);
}
return (await response.json()).data;
}
};
export {
habitica,
opts
};

View File

@@ -1,105 +0,0 @@
import { Mize } from "@ppc/marts/stub_deno";
export const opts = {
"habitica.api_url": {
env_var_name: "PPC_HABITICA_API_URL",
default_val: "https://habitica.com/api/v3",
},
"habitica.user_id": {
env_var_name: "PPC_HABITICA_USER_ID",
},
"habitica.client_name": {
default_val: "3544a0b8-d71a-46e0-9bb1-6ddbb2abcddb-PPC-Software",
},
"habitica.api_token": {
env_var_name: "PPC_HABITICA_API_TOKEN",
},
};
export async function habitica(mize: Mize) {
console.log("hiiiiiiiiiiiiiiiiiiii");
mize.add_opts(opts);
mize.add_part(new Habitica(mize));
}
async function handleRateLimit(response) {
const limit = response.headers.get("X-RateLimit-Limit") || "NONE";
const remaining = parseInt(
response.headers.get("X-RateLimit-Remaining") || "10",
10,
);
const reset = response.headers.get("X-RateLimit-Reset");
console.log(
`RateLimit: ${limit} | Remaining: ${remaining} | Reset: ${reset}`,
);
if (remaining < 2 && reset) {
const resetDate = new Date(reset);
const now = new Date();
const waitMs = resetDate.getTime() - now.getTime() + 1000;
if (waitMs > 0) {
console.log(
`Waiting ${Math.round(waitMs / 1000)} secs for next rate limit window...`,
);
await new Promise((resolve) => setTimeout(resolve, waitMs));
}
}
}
class Habitica {
mize: Mize;
constructor(mize: Mize) {
this.mize = mize;
}
async get_tasks(type: string = "todos") {
return await this.api_request("GET", `tasks/user?type=${type}`);
}
async delete_task(id: string) {
await this.api_request("DELETE", `tasks/${id}`);
}
async api_request(
method: string,
path: string,
extraHeaders: object = {},
data: object = {},
) {
const mize = this.mize;
if (!path.startsWith("/")) {
path = "/" + path;
}
const headers = Object.assign(
{
"Content-Type": "application/json",
"x-api-user": mize.get_config("habitica.user_id"),
"x-api-key": mize.get_config("habitica.api_token"),
"x-client": mize.get_config("habitica.client_name"),
},
extraHeaders,
);
const response = await fetch(mize.get_config("habitica.api_url") + path, {
method,
headers,
body: JSON.stringify(data),
});
await handleRateLimit(response);
if (!response.ok) {
console.log("request_url:", mize.get_config("habitica.api_url") + path);
throw new Error(
`Habitica API error: ${response.status} ${response.statusText}`,
);
}
return (await response.json()).data;
}
}

View File

@@ -1,2 +0,0 @@
export { task_obsidian_otask_parse } from "./task/obsidian-otask-parse";
export { c2vi_obsidian_canvas_patch } from "./c2vi/obsidian-canvas-patch";

View File

@@ -1,35 +0,0 @@
use deno_core::extension;
use deno_core::op2;
use deno_core::OpState;
use mize::{Mize, MizeError};
use std::sync::Arc;
#[op2(fast)]
fn op_mize_get_part(#[string] name: &str) {
println!("js wants the part {}", name);
}
#[op2]
#[string]
fn op_mize_get_config(state: &mut OpState, #[string] key: &str) -> String {
let mize: Mize = state.borrow::<Mize>().clone();
format!("config_value_for_{}", key)
}
#[op2(fast)]
fn op_mize_add_part(state: &mut OpState, #[string] key: &str) {
let mize: Mize = state.borrow::<Mize>().clone();
println!("adding part {}", key);
}
// Create an extension with your custom ops
extension!(
my_extension,
ops = [op_mize_get_part, op_mize_get_config],
options = {
mize: Mize,
},
state = |state: &mut OpState, config: Config| {
state.put::<Mize>(config.mize);
},
);

View File

@@ -1,225 +0,0 @@
use deno_core::error::AnyError;
use deno_core::url::Url;
use deno_core::{extension, op2, JsRuntime, ModuleSpecifier, PollEventLoopOptions, RuntimeOptions};
use deno_core::{FastStaticString, FsModuleLoader};
use mize::async_trait;
use mize::instance::MizePartCreate;
use mize::{mize_part, Mize, MizePart, MizeResult};
use std::future::Future;
use std::path::Path;
use std::pin::Pin;
use std::rc::Rc;
use std::str::FromStr;
use tokio::runtime::Builder;
use tokio::task::{spawn_local, LocalSet};
mod glue_deno;
#[mize_part("js")]
#[derive(Default)]
pub struct JsPart {
mize: Mize,
closure_sender: Option<flume::Sender<Box<dyn FnOnce(&mut JsRuntime) -> MizeResult<()> + Send>>>,
async_closure_sender: Option<flume::Sender<BoxClosure>>,
}
pub type BoxFuture<'a> = Pin<Box<dyn Future<Output = MizeResult<()>> + 'a>>;
pub type BoxClosure = Box<dyn for<'a> FnOnce(&'a mut JsRuntime) -> BoxFuture<'a> + Send + 'static>;
impl MizePart for JsPart {
fn init(&mut self, mize: &mut Mize) -> MizeResult<()> {
println!("js part init");
Ok(())
}
}
pub fn js(mize: &mut Mize) -> MizeResult<()> {
let (closure_sender, closure_receiver) =
flume::unbounded::<Box<dyn FnOnce(&mut JsRuntime) -> MizeResult<()> + Send>>();
let (async_closure_sender, async_closure_receiver) = flume::unbounded::<BoxClosure>();
let mize_clone = mize.clone();
// the thread which will run any js
mize.spawn("js_runtime_thread", || {
js_runtime_thread(mize_clone, closure_receiver, async_closure_receiver)
})?;
mize.add_part(Box::new(JsPart {
mize: mize.clone(),
closure_sender: Some(closure_sender),
async_closure_sender: Some(async_closure_sender),
}))
}
pub fn part_from_file(
mize: &mut Mize,
name: &'static str,
code: FastStaticString,
) -> MizeResult<()> {
let mut js = mize.get_part_native::<JsPart>("js")?;
js.with_runtime_async(move |runtime: &mut JsRuntime| {
let module_spec = ModuleSpecifier::from_str(name).unwrap();
return Box::pin(async move {
runtime
.load_side_es_module_from_code(&module_spec, code)
.await;
Ok(())
});
});
Ok(())
}
impl JsPart {
/*
pub fn part_from_js_file(
&mut self,
name: &'static str,
path: &'static str,
) -> Box<dyn MizePart + Send + Sync> {
Box::new(PartFromJsFileAdapter {
mize: self.mize.clone(),
name,
js_file: path,
})
}
*/
pub fn with_runtime<T: FnOnce(&mut JsRuntime) -> MizeResult<()> + Send + 'static>(
&mut self,
func: T,
) {
self.closure_sender
.as_mut()
.unwrap()
.send(Box::new(func))
.unwrap();
}
pub fn with_runtime_async<F>(&mut self, func: F)
where
F: for<'a> FnOnce(&'a mut JsRuntime) -> BoxFuture<'a> + Send + 'static,
{
self.async_closure_sender
.as_mut()
.unwrap()
.send(Box::new(func))
.unwrap();
}
}
/*
#[mize_part]
#[derive(Default)]
struct PartFromJsFileAdapter {
mize: Mize,
name: &'static str,
js_file: &'static str,
}
#[async_trait]
impl MizePart for PartFromJsFileAdapter {
fn deps(&self) -> &'static [&'static str] {
&["js"]
}
fn name(&self) -> &'static str {
self.name
}
async fn async_init(&mut self, mize: &mut Mize) -> MizeResult<()> {
let js = mize.get_part_native::<JsPart>("js")?;
let path = self.js_file;
let js_code = format!(
r#"
import {{ opts, deps, create }} from "{path}";
const isPromise = (value) => {{
return !!value && (typeof value === 'object' || typeof value === 'function') && typeof value.then === 'function';
}};
// Call the async function
let create_result = create(mize);
if (isPromise(create_result)) {{
create_result = await create_result;
}}
const result = {{
opts: opts(mize),
deps: deps(mize),
create: create_result,
}};
result;
"#
);
// Execute the script
let result = js.runtime().execute_script("[main]", js_code)?;
// Resolve the promise and run event loop
let resolved_value = js.runtime().resolve_value(result).await?;
// Run the event loop to completion
js.runtime()
.run_event_loop(PollEventLoopOptions::default())
.await?;
println!("Execution of part {} completed successfully", self.name());
Ok(())
}
async fn async_run(&mut self, mize: &mut Mize) -> MizeResult<()> {
let js = mize.get_part_native::<JsPart>("js")?;
let path = self.js_file;
let js_code = format!(
r#"
import {{ run }} from "{path}";
const result = run()
result;
"#
);
// Execute the script
let result = js.runtime().execute_script("[main]", js_code)?;
// Resolve the promise and run event loop
let resolved_value = js.runtime().resolve_value(result).await?;
// Run the event loop to completion
js.runtime()
.run_event_loop(PollEventLoopOptions::default())
.await?;
println!("Running of part {} completed successfully", self.name());
Ok(())
}
}
*/
fn js_runtime_thread(
mize: Mize,
closure_receiver: flume::Receiver<Box<dyn FnOnce(&mut JsRuntime) -> MizeResult<()> + Send>>,
async_closure_receiver: flume::Receiver<BoxClosure>,
) -> MizeResult<()> {
let mut js_runtime = JsRuntime::new(RuntimeOptions {
module_loader: Some(Rc::new(FsModuleLoader)),
extensions: vec![glue_deno::my_extension::init(mize.clone())],
..Default::default()
});
let poll_opts = PollEventLoopOptions::default();
let tokio_runtime = Builder::new_current_thread().enable_all().build().unwrap();
let local = LocalSet::new();
local.block_on(&tokio_runtime, async move {
loop {
// Drain queued closures quickly (no await here if possible)
if let Ok(func) = closure_receiver.recv_async().await {
if let Err(e) = func(&mut js_runtime) {
mize.report_err(e);
}
}
// Drive JS one tick
let _ = futures::future::poll_fn(|cx| js_runtime.poll_event_loop(cx, poll_opts)).await;
}
});
Ok(())
}

View File

@@ -1,10 +0,0 @@
export class Mize {
async get_config(key: string): Promise<any> {
return "hiiiiiii";
}
async add_opts(opts: any): Promise<void> {}
async add_part(part: any): Promise<void> {}
async report_err(err: MizeError) {}
}
type MizeError = string;

View File

@@ -1,17 +0,0 @@
#[cfg(feature = "target-os")]
mod cli;
#[cfg(feature = "target-os")]
pub use cli::cli;
mod js;
#[cfg(feature = "target-os")]
pub fn habitica(mize: &mut mize::Mize) {
use deno_core::{ascii_str_include, include_js_files};
js::part_from_file(
mize,
"habitica",
ascii_str_include!("./deno_dist/habitica.js"),
);
}

View File

@@ -1,27 +0,0 @@
{
"name": "marts",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"packageManager": "pnpm@10.15.1",
"dependencies": {
"@flowershow/remark-wiki-link": "^3.4.0",
"builtin-modules": "^5.0.0",
"esbuild": "^0.27.3",
"mdast": "^3.0.0",
"obsidian": "^1.12.2",
"remark-frontmatter": "^5.0.0",
"remark-gfm": "^4.0.1",
"remark-parse": "^11.0.0",
"remark-stringify": "^11.0.0",
"typescript": "^5.9.3",
"unified": "^11.0.5",
"unist-util-visit": "^5.1.0"
}
}

View File

@@ -1,522 +0,0 @@
import { id, i, init, InstaQLEntity } from "@instantdb/react";
import remarkParse from "remark-parse";
import remarkStringify from "remark-stringify";
import { unified } from "unified";
import { visit } from "unist-util-visit";
import { Root, Content, Heading, List, ListItem, Text } from "mdast";
import remarkFrontmatter from "remark-frontmatter";
import remarkGfm from "remark-gfm";
import wikiLinkPlugin from "@flowershow/remark-wiki-link";
const OTASK_PROP_NAMES = ["short", "outcome", "priority"];
const ACTION_PROP_NAMES = [];
const REMARK = unified()
.use(remarkParse)
.use(remarkFrontmatter)
.use(remarkGfm)
.use(remarkStringify, { bullet: "-" });
export function task_obsidian_otask_parse(mize: any) {
const obsidian_plugin = mize.get_part_native("obsidian");
obsidian_plugin.addCommand({
id: "full-parse",
name: "Parse all otask files into the db",
callback: async () => {
await full_parse();
},
});
}
async function full_parse() {
console.log("full parse.......................... yay");
let otasks = [];
OTASK_PARSE_QUEUE = [];
OTASK_LIST = [];
const root_file = app.vault.getAbstractFileByPath("task/my-projects.md");
const content = await app.vault.read(root_file);
const list = await getListUnderHeading(content, "root");
for (const entry of list) {
queue_otasks_from_list_item(entry, []);
}
// go through the queue of otaskParseTasks
// using a queue instead of recursive calls, to make it more debuggable...
do {
const task = OTASK_PARSE_QUEUE.shift();
dbg("task", task);
if (task.file) {
const otask = await otasks_from_file(
task.file,
task.otask_path,
task.priority,
);
dbg("otask", otask);
if (otask) {
OTASK_LIST.push(otask);
}
} else {
const otask = await otasks_from_list_item(
task.listItem,
task.otask_path,
task.priority,
);
dbg("otask", otask);
if (otask) {
OTASK_LIST.push(otask);
}
}
} while (OTASK_PARSE_QUEUE.length > 0);
console.log("otasks::", OTASK_LIST);
render_sub0_file(OTASK_LIST);
}
let OTASK_PARSE_QUEUE = [];
let OTASK_LIST = [];
let DBG = false;
function dbg() {
if (DBG) {
console.log(arguments);
}
}
function queue_otasks_from_list_item(listItem, otask_path, priority = 0) {
OTASK_PARSE_QUEUE.push({
listItem,
otask_path,
priority,
});
}
function queue_otasks_from_file(file, otask_path, priority = 0) {
OTASK_PARSE_QUEUE.push({
file,
otask_path,
priority,
});
}
// parsing helper functions
async function render_sub0_file(otasks) {
const sub0_file = app.vault.getAbstractFileByPath("private/sub0-tasks.md");
if (!(sub0_file instanceof TFile)) {
console.error(
"SystemC2: sub0_file not found:",
"private/sub0-tasks.md",
sub0_file,
);
return;
}
//render sub0
const sub0_otasks = otasks.filter(
(otask) => otask.priority == 0 && !otask.is_file,
);
console.log("sub0 otasks...", sub0_otasks);
let rendered_otasks_string = "";
for (const otask of sub0_otasks) {
const path_part = otask.path.length > 0 ? otask.path.join(": ") + ": " : "";
let content_part = otask.content ? otask.content + "\n" : "";
content_part = content_part
.split("\n")
.map((line) => "\t" + line)
.join("\n"); // add a \t in front of every otask.content line
rendered_otasks_string += `- ${path_part}${otask.longName}\n${content_part}\n`;
}
//console.log("newContent...", rendered_otasks_string)
await replaceHeading(sub0_file, "sub0", rendered_otasks_string);
//render sub-1
}
async function replaceHeading(
file: TFile,
heading: string,
newContent: string,
) {
const data = await this.app.vault.read(file);
// Parse the Markdown into an AST
const tree = REMARK.parse(data) as Root;
// Split new content into AST nodes
const newContentAst = REMARK.parse(newContent) as Root;
const children = tree.children;
const newChildren: Content[] = [];
let insideTarget = false;
let targetLevel = 0;
for (let i = 0; i < children.length; i++) {
const node = children[i];
if (node.type === "heading") {
const headingNode = node as Heading;
const text = headingNode.children
.filter((c) => c.type === "text")
.map((c: any) => c.value)
.join("");
// If we encounter our target heading
if (text.trim() === heading) {
insideTarget = true;
targetLevel = headingNode.depth;
newChildren.push(node); // Keep the heading itself
// Insert new content right after
newChildren.push(...newContentAst.children);
continue;
}
// If we’re currently inside target section and see a heading of same or higher level
if (insideTarget && headingNode.depth <= targetLevel) {
insideTarget = false;
}
}
// If we’re not inside the target section, keep the node
if (!insideTarget) {
newChildren.push(node);
}
}
const newTree: Root = { ...tree, children: newChildren };
// Convert AST back to Markdown
const newMarkdown = await REMARK.stringify(newTree);
await this.app.vault.modify(file, newMarkdown);
}
async function otasks_from_list_item(listItem, otask_path, priority = 0) {
// the main otask this listItem is about
let otask = {
description: [],
actions: [],
path: [],
priority,
};
dbg("SystemC2: otasks_from_list_item", listItem, otask_path);
if (!listItem.children[0].type === "paragraph") {
dbg(
"listItem[0] of a otask item is not a paragraph... the otask:",
listItem,
);
return null;
}
let longName = nodeToString(listItem.children[0]);
dbg("longName:", longName);
// check for [[anotherotask]], call otasks_from_file, return those
const match = longName.match(/\[\[([^\]]+)\]\]/);
if (match) {
const file = app.metadataCache.getFirstLinkpathDest(match[1], "");
if (file) {
const metadata = app.metadataCache.getFileCache(file);
if (metadata?.frontmatter?.tags?.includes("t/otask")) {
queue_otasks_from_file(file, otask_path);
return null;
} else {
dbg(
"SystemC2: listItem with Link to '",
match[1],
"', which is not an otask",
longName,
);
}
}
}
//check for [x] and remove from name
if (longName.startsWith("[ ") || longName.startsWith("[x")) {
longName = longName.slice(4);
}
//check for "ot:" and remove from name
if (longName.startsWith("ot:")) {
longName = longName.slice(4);
}
otask.longName = longName;
otask.short = longName;
otask.path = otask_path;
let new_otask_path = [...otask_path, otask.short];
// go through sub list items
// - if it's a prop for the otask... handle that
// - if it's an "ac:" handle that
// - if it's "ot: " handle as another otask -> recursive call
// - if it's "[ ]" handle as another otask -> recursive call
// - if it's a link to a otask file... handle as another otask -> call otasks_from_file()
// - else add it as description item list to otask
if (!listItem.children || listItem.children.length < 2) {
return otask;
}
// add content as text to otask
if (otask.longName == "hosting updates") {
console.log("listItem... ", listItem.children[1]);
let hii = REMARK.stringify(listItem.children[1]);
console.log("text...", hii);
}
let content_text = REMARK.stringify(listItem.children[1]);
otask.content = content_text;
for (const subListItem of listItem.children[1].children) {
let text = nodeToString(subListItem.children[0]);
// check prop
if (check_otask_prop_from_list_item(subListItem, otask)) {
new_otask_path = [...otask_path, otask.short]; // have to redo it here, in case short changed from aprop
continue; // don't do any other processing on this sub-listItem
}
// remove "[ ]"
if (text.startsWith("[ ") || text.startsWith("[x")) {
text = text.slice(4);
}
// check ac
if (text.startsWith("ac:")) {
otask.actions = [
...otask.actions,
action_from_list_item(subListItem, new_otask_path, otask.priority),
];
continue; // don't do any other processing on this sub-listItem
}
// check ot
if (text.startsWith("ot:")) {
queue_otasks_from_list_item(subListItem, new_otask_path, otask.priority);
continue; // don't do any other processing on this sub-listItem
}
// check [ ]
if (text.startsWith("[ ] ") || text.startsWith("[x] ")) {
queue_otasks_from_list_item(subListItem, new_otask_path, otask.priority);
continue; // don't do any other processing on this sub-listItem
}
// check link to otask file
const match = text.match(/\[\[([^\]]+)\]\]/);
if (match) {
const file = app.metadataCache.getFirstLinkpathDest(match[1], "");
if (file) {
const metadata = app.metadataCache.getFileCache(file);
if (metadata?.frontmatter?.tags?.includes("t/otask")) {
queue_otasks_from_file(file, new_otask_path, otask.priority);
continue;
} else {
dbg(
"SystemC2: listItem with Link to '",
match[1],
"', which is not an otask",
longName,
);
}
continue; // don't do any other processing on this sub-listItem
}
}
// add as description
const desc = REMARK.stringify(subListItem);
otask.description.push(desc);
}
return otask;
}
async function otasks_from_file(file: TFile, otask_path, priority = 0) {
dbg("SystemC2: otasks_from_file", file, otask_path);
// the main otask this file is about
let otask = {
description: [],
actions: [],
path: [],
priority,
is_file: true,
};
otask.longName = file.basename;
otask.short = otask.longName;
otask.path = otask_path;
let new_otask_path = [...otask_path, otask.short];
const content = await app.vault.read(file);
// parse listItems at the top... for props
const topListItems = getListItemsBeforeFirstHeading(content);
for (const topListItem of topListItems) {
const text = nodeToString(topListItem);
// check for prop
if (check_otask_prop_from_list_item(topListItem, otask)) {
new_otask_path = [...otask_path, otask.short];
continue; // don't do any other processing on this sub-listItem
}
}
// parse all the sub sections
const SUB_COUNT = 10;
for (let sub_number = 0; sub_number < SUB_COUNT; sub_number++) {
let listItems = await getListUnderHeading(content, "sub" + sub_number);
// "sub" without a number should have priority 5
if (sub_number == 5) {
listItems = listItems.concat(await getListUnderHeading(content, "sub"));
}
//dbg(`sub section ${sub_number} has items:`, listItems)
for (const listItem of listItems) {
// check if listItem is an action...
let text = nodeToString(listItem.children[0]);
if (
text.startsWith("ac:") ||
text.startsWith("[ ] ac:") ||
text.startsWith("[x] ac:")
) {
const action = action_from_list_item(listItem, otask_path);
otask.actions.push(action);
}
queue_otasks_from_list_item(
listItem,
new_otask_path,
otask.priority + sub_number,
);
}
}
return otask;
}
function action_from_list_item(listItem, otask_path) {
dbg("SystemC2: action_from_list_item", listItem, otask_path);
let action = {};
let text = nodeToString(listItem.children[0]).slice(4);
if (text.startsWith("[")) {
text = text.slice(4);
}
action.name = text;
return action;
}
function check_otask_prop_from_list_item(listItem, otask) {
let text = nodeToString(listItem.children[0]);
for (const prop_name of OTASK_PROP_NAMES) {
if (text.startsWith(prop_name + ":")) {
const value = text.slice(prop_name.length + 2);
if (prop_name == "priority") {
// parse priority as int
otask[prop_name] = parseInt(value);
} else if (prop_name == "outcome") {
// add sub items to it
let fullText = REMARK.stringify(listItem).slice(prop_name.length + 4);
otask[prop_name] = fullText;
} else {
otask[prop_name] = value;
}
return true;
}
}
return false;
}
// Extract plain text from a node
function nodeToString(node: any): string {
let result = "";
visit(node, "text", (textNode: Text) => {
result += textNode.value;
});
return result.trim();
}
async function getListUnderHeading(
markdown: string,
headingText: string,
): string[] {
const tree = REMARK.parse(markdown);
//dbg("tree", tree)
let capture = false;
let items: string[] = [];
for (let i = 0; i < tree.children.length; i++) {
const node: Content = tree.children[i];
// Detect the target heading
if (
node.type === "heading" &&
nodeToString(node).toLowerCase() === headingText.toLowerCase()
) {
capture = true;
continue;
}
// Stop if another heading of the same or higher level comes
if (capture && node.type === "heading") {
break;
}
// Collect list items if capturing
if (capture && node.type === "list") {
for (const li of (node as List).children) {
if (li.type === "listItem") {
items.push(li);
}
}
}
}
return items;
}
function getListItemsBeforeFirstHeading(markdown: string) {
const tree = REMARK.parse(markdown);
let items = [];
for (const node of tree.children) {
if (node.type === "heading") {
// stop at first heading
break;
}
if (node.type === "list") {
for (const li of (node as List).children) {
if (li.type === "listItem") {
items.push(li);
}
}
}
}
return items;
}

2886
packages/mize/Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,97 +0,0 @@
[package]
name = "mize"
version = "0.0.1"
edition = "2021"
license = "GPL-3.0"
description = 'A strongly typed "filesystem" for the age of connectivity.'
repository = "https://github.com/c2vi/mize"
[package.metadata.wasm-pack.profile.dev]
wasm-opt = false
[lib]
name = "mize"
path = "src/lib.rs"
crate-type = ["cdylib", "rlib"]
[[bin]]
name = "mize"
path = "src/platform/os/main.rs"
required-features = [ "target-os" ]
[features]
default = [ "target-os" ]
target-os= [ "clap", "home", "nix", "sysinfo", "ciborium/default", "tracing-subscriber", "async", "tracing-subscriber/env-filter", "libloading", "ciborium-io", "tracing-core", "tar", "flate2", "http_req" ]
target-wasm = [ "wasm-bindgen", "console_error_panic_hook", "web-sys", "web-sys/Worker", "web-sys/Window", "web-sys/Request", "web-sys/RequestInit", "web-sys/RequestMode", "web-sys/Response", "web-sys/WorkerOptions", "web-sys/WorkerType", "serde-wasm-bindgen", "wasm-bindgen-futures", "wee_alloc"]
async = ["tokio/net", "tokio", "tokio/rt-multi-thread", "tokio/io-util"]
[dependencies]
# core dependencies
colored = "2.0.0"
uuid = "1.3.0"
ciborium = { version = "0.2.2", default-features = false }
serde = "1.0.203"
interner = { git = "https://github.com/c2vi/interner" }
tracing = "0.1.40"
serde_json = { version = "1.0.117", optional = false }
toml = { version = "0.8.13", features = ["parse"], optional = false }
dyn-clone = "1.0.17"
sha2 = "0.10.8"
flume = "0.11.1"
mize_macros = { path = "../ac_mize_macros" }
async-trait = "0.1.89"
# builtin modules
#mize_module_blob = { path = "./modules/modules/Blob" }
# wasm dependencies
wasm-bindgen = { version = "0.2.84", optional = true }
console_error_panic_hook = { version = "0.1.7", optional = true }
web-sys = { version = "0.3.70", optional = true }
serde-wasm-bindgen = { version = "0.6.5", optional = true }
wasm-bindgen-futures = { version = "0.4.49", optional = true }
wee_alloc = { version = "0.4.5", optional = true }
# os dependencies
clap = { version = "4.4.11", features = ["cargo"], optional = true }
home = { version = "0.5.9", optional = true }
daemonize = { version = "0.5.0", optional = true }
nix = { version = "0.27.1", features = ["signal"], optional = true }
sysinfo = { version = "0.30.12", optional = true }
tracing-subscriber = { version = "0.3.18", optional = true }
tokio = { version = "1.46.1", features = ["sync"], optional = true }
libloading = { version = "0.8.5", optional = true }
ciborium-io = { version = "0.2.2", optional = true }
tracing-core = { version = "0.1.32", optional = true }
tar = { version = "0.4.42", optional = true }
flate2 = { version = "1.0.33", optional = true }
http_req = { version = "0.12.0", default-features = false, features = [ "rust-tls" ], optional = true }
#http_req = { git = "https://github.com/c2vi/http_req", optional = true, default-features = false, features = [ "native-tls" ] }
# bare dependencies
# unused dependencies
axum = { version = "0.6.1", features = ["ws", "headers"], optional = true }
axum-extra = { version = "0.4.2", features = ["spa"], optional = true }
axum-macros = { version = "0.3.0", optional = true }
#tokio-core = "0.1.18"
#tokio-io = "0.1.13"
#tokio-stream = { version = "0.1.9", optional = true }
#derive_more = "0.99.17"
#futures-util = "0.3.24"
#itertools = "0.10.5"
#lazy_static = "1.4.0"
#rmp-serde = "1.1.1"
#rmpv = { version = "1.0.0", features = ["serde", "with-serde"] }
#toml = "0.5.10"
#tower-http = "0.3.5"
[dev-dependencies]
wasm-bindgen-test = { version = "0.3.34" }
[profile.release]
# Tell `rustc` to optimize for small code size.
opt-level = "s"

View File

@@ -1,5 +0,0 @@
{
"name": "@ppc/mize",
"imports": {},
"exports": {}
}

View File

@@ -1,45 +0,0 @@
set -e
if [[ "$1" == "a" ]]
then
ssh acern mkdir -p /tmp/mize-src
ssh acern mkdir -p ~/work/modules
rsync -rv --exclude="target" --exclude="gitignore" ./* acern:/tmp/mize-src/
rsync -rv --exclude="target" --exclude="gitignore" --exclude="node_modules" ~/work/modules acern:work/
path=$(ssh acern /home/me/out/nix/bin/nix build /tmp/mize-src#dist -L --print-out-paths)
echo doneeeeeeeeeeeeeee building
echo path: $path
#scp -r acern:$path/* ocih:host/webserver/webfiles
tmp=$(mktemp -d)
[[ "$path" != "" ]] && rsync -rv -p --chmod=u+w acern:$path/* $tmp
[[ "$path" != "" ]] && rsync -rv $tmp/* ocih:host/data/my-website --rsync-path="sudo rsync"
elif [[ "$1" == "c" ]] # only copy files to acern
then
ssh acern mkdir -p /tmp/mize-src
ssh acern mkdir -p ~/work/modules
rsync -rv --exclude="target" --exclude="gitignore" ./* acern:/tmp/mize-src/
rsync -rv --exclude="target" --exclude="gitignore" --exclude="node_modules" ~/work/modules acern:work/
else
path=$(nix build .#dist -L -v --print-out-paths $@)
echo doneeeeeeeeeeeeeee building
echo path: $path
#scp -r $path/* ocih:host/webserver/webfiles
[[ "$path" != "" ]] && rsync -rv $path/* ocih:host/data/my-website --rsync-path="sudo rsync"
fi

View File

@@ -1,2 +0,0 @@
nix develop .#mizeFor.x86_64-linux-gnu.main.devShell --override-input nixpkgs github:NixOS/nixpkgs/ea6033ce4ddf2c0ede2ea147a450d4f880796128

View File

@@ -1,224 +0,0 @@
{
inputs = {
# nixpkgs 35.11 still contains rust 1.73
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
fenix = {
url = "github:nix-community/fenix";
inputs.nixpkgs.follows = "nixpkgs";
};
crane = {
url = "github:ipetkov/crane";
};
rust-overlay = {
url = "github:oxalica/rust-overlay";
inputs.nixpkgs.follows = "nixpkgs";
};
mize_modules = {
url = "github:c2vi/mize-modules?submodules=1";
flake = false;
};
flake-utils.url = "github:numtide/flake-utils";
};
outputs = { self, flake-utils, nixpkgs, fenix, crane, mize_modules, rust-overlay, ... }@inputs: flake-utils.lib.eachDefaultSystem (system:
############################## LET BINDINGS ##############################
let
pkgs = nixpkgs.legacyPackages.${system};
wasmToolchain = fenix.packages.${system}.combine [
fenix.packages.${system}.targets.wasm32-unknown-unknown.stable.toolchain
fenix.packages.${system}.stable.toolchain
];
wasmCrane = (crane.mkLib pkgs).overrideToolchain wasmToolchain;
osToolchain = fenix.packages.${system}.nightly.toolchain;
osCrane = (crane.mkLib pkgs).overrideToolchain osToolchain;
#winToolchain = fenix.packages.${system}.combine [
#fenix.packages.${system}.targets.x86_64-pc-windows-gnu.latest.toolchain
#fenix.packages.${system}.latest.toolchain
#];
winToolchain = with fenix.packages.${system};
combine [
minimal.rustc
minimal.cargo
targets.x86_64-pc-windows-gnu.latest.rust-std
];
winCrane = (crane.mkLib pkgs).overrideToolchain winToolchain;
wasmArtifacts = wasmCrane.buildDepsOnly ({
src = self;
doCheck = false; # tests does not work in wasm
CARGO_BUILD_TARGET = "wasm32-unknown-unknown";
cargoExtraArgs = "--features target-wasm --no-default-features";
RUSTFLAGS="-C linker=wasm-ld";
buildInputs = with pkgs; [ cargo-binutils lld ];
});
defaultMizeConfig = {
config = {
namespace = "mize.buildtime.ns";
module_url = "c2vi.dev";
};
};
#systems = [ "aarch64-linux" "x86_64-linux" "x86_64-pc-windows-gnu" "wasm32-unknown-none-unknown" "x86_64-apple-darwin" "aarch64-apple-darwin" ];
systems = [ "aarch64-unknown-linux-gnu" "x86_64-unknown-linux-gnu" "x86_64-pc-windows-gnu" "wasm32-unknown-none-unknown" ];
mizeLib = import ./lib.nix {
inherit inputs nixpkgs pkgs self osCrane defaultMizeConfig mize_modules;
inherit rust-overlay crane fenix;
buildSystem = system;
stdenv = pkgs.stdenv;
};
in {
############################## PACKAGES ##############################
packages = rec {
inherit osCrane;
#test = mizeLib.buildMizeForSystem "wasm32-unknown-none-unknown";
test = mizeLib.buildMizeForSystem "aarch64-linux";
#test = mizeLib.buildMizeForSystem "x86_64-pc-windows-gnu";
mizeFor = let
mizes = map mizeLib.buildMizeForSystem systems;
in builtins.listToAttrs ( map ( mize: { name = mize.system.name; value = mize; } ) mizes );
dist = mizeLib.dist systems;
random = mizeLib.random systems;
inherit mizeLib;
#pkgsCross = import nixpkgs { buildSystem = system; crossSystem = { config = "x86_64-pc-windows-gnu"; }; overlays = [ rust-overlay.overlays.default ]; };
pkgsTest = pkgs;
#pkgsCross = import nixpkgs { buildSystem = system; crossSystem = { config = "x86_64-w64-windows-mingw"; }; overlays = [ rust-overlay.overlays.default ]; };
pkgsCross = import nixpkgs { buildSystem = system; crossSystem = { config = "x86_64-unknown-linux-gnu"; }; overlays = [ rust-overlay.overlays.default ]; };
craneLib = (crane.mkLib pkgsCross).overrideToolchain (p: p.rust-bin.stable.latest.default);
one = craneLib.buildPackage {
src = "${self}";
#cargoExtraArgs = "--bin mize --features target-os";
cargoExtraArgs = "--bin mize";
doCheck = false; # tests does not work in wasm
CARGO_BUILD_TARGET = "x86_64-pc-windows-gnu";
MIZE_BUILD_CONFIG = pkgs.writeTextFile {
name = "mize-build-config";
text = builtins.toJSON (defaultMizeConfig // {
mize_version = one.version;
});
};
};
default = osCrane.buildPackage {
src = "${self}";
cargoExtraArgs = "--bin mize --features target-os";
};
wasm = wasmCrane.buildPackage {
src = "${self}";
CARGO_BUILD_TARGET = "wasm32-unknown-unknown";
cargoExtraArgs = "--bin mize --features target-wasm ";
doCheck = false;
};
inherit wasmArtifacts;
# Thanks to:
# - https://github.com/dl-solarity/hardhat-diamond-tools/blob/e5b8bc9624fbf89eda83a8ce4f6b6672ea83f550/flake.nix#L103
# - https://github.com/ipetkov/crane/issues/362#issuecomment-1683220603
npmPackage = wasmCrane.mkCargoDerivation {
name = "mize-npm-package";
# i can't use CARGO_BUILD_TARGET to set the target
# because then the cargo install run by wasm-pack tries to build the wasm-bindgen-cli for a wasm target...
src = self;
cargoArtifacts = wasmArtifacts;
doCheck = false;
buildPhaseCargoCommand = ''
echo build hiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii
mkdir -p $out/pkg
export HOME=$(mktemp -d fake-homeXXXX)
wasm-pack build --out-dir $out/pkg --scope=c2vi -- --no-default-features --features target-wasm
'';
buildInputs = with pkgs; [ wasm-bindgen-cli binaryen wasm-pack wasmToolchain ];
};
};
############################## DEV SHELLS ##############################
devShells = {
modules = mizeLib.moduleShells system;
modulesCross = pkgs.lib.genAttrs systems (system: mizeLib.moduleShells system);
one = pkgs.stdenv.mkDerivation {
name = "hiiiiiiii";
nativeBuildInputs = [
(fenix.packages.${system}.combine [
fenix.packages.${system}.targets.wasm32-unknown-unknown.latest.toolchain
fenix.packages.${system}.latest.toolchain
fenix.packages.${system}.latest.rust-src
])
];
};
win = winCrane.buildPackage {
#src = winCrane.cleanCargoSource ./testing;
#pname = "testing";
src = ./testing;
strictDeps = true;
doCheck = false;
CARGO_BUILD_TARGET = "x86_64-pc-windows-gnu";
# fixes issues related to libring
TARGET_CC = "${pkgs.pkgsCross.mingwW64.stdenv.cc}/bin/${pkgs.pkgsCross.mingwW64.stdenv.cc.targetPrefix}cc";
#fixes issues related to openssl
OPENSSL_DIR = "${pkgs.openssl.dev}";
OPENSSL_LIB_DIR = "${pkgs.openssl.out}/lib";
OPENSSL_INCLUDE_DIR = "${pkgs.openssl.dev}/include/";
depsBuildBuild = with pkgs; [
pkgsCross.mingwW64.stdenv.cc
pkgsCross.mingwW64.windows.pthreads
];
shellHook = ''
export MIZE_CONFIG_FILE=${self}/test-config.toml
'';
MIZE_BUILD_CONFIG = pkgs.writeTextFile {
name = "vic-build-config";
text = builtins.toJSON defaultMizeConfig;
};
};
default = (mizeLib.buildMizeForSystem system).modules.mize.devShell;
};
}) // {
############################## SOME GLOBAL OUTPUTS ##############################
inherit inputs self;
rustPkgs = import nixpkgs { overlays = [rust-overlay.overlays.default]; system = "x86_64-linux"; };
};
}

View File

@@ -1,393 +0,0 @@
{ pkgs
, osCrane
, self
, defaultMizeConfig
, mize_modules
, buildSystem
, nixpkgs
, rust-overlay
, crane
, fenix
, stdenv
, lib ? pkgs.lib
, ...
}:
rec {
getCrossSystem = system: let
hostSystem = (if builtins.isString system then (pkgs.lib.systems.parse.mkSystemFromString system) else system)
// rec {
name = if hostSystem.vendor != "unknown"
then "${hostSystem.cpu.name}-${hostSystem.kernel.name}-${hostSystem.abi.name}"
else "${hostSystem.cpu.name}-${hostSystem.vendor.name}-${hostSystem.kernel.name}-${hostSystem.abi.name}";
nameFull = "${hostSystem.cpu.name}-${hostSystem.vendor.name}-${hostSystem.kernel.name}-${hostSystem.abi.name}";
nameRust = nameFull;
config = nameFull;
isCross = system != buildSystem;
isNative = system == buildSystem;
};
in hostSystem;
# get a list of all modules
# aka folders with a mize.nix in them
module_list_drv = path: pkgs.stdenv.mkDerivation {
name = "mize_module_list";
dontUnpack = true;
configurePhase = "";
buildPhase = ''
mkdir -p $out
touch $out/modules_to_build
find ${path} -not -type d -name mize.nix | while read p; do
(echo "$p" >> $out/modules_to_build; echo "found module $p")
done
exit 0
'';
};
findModules = path: let
mize_module_no_externals = builtins.getEnv "MIZE_MODULE_NO_EXTERNALS";
module_list_string = builtins.readFile "${module_list_drv path}/modules_to_build";
module_list = pkgs.lib.lists.remove "" (pkgs.lib.strings.splitString "\n" module_list_string);
getExternals = path: map findModules ((pkgs.callPackage ((import path).externals or (args: [])) {}));
in module_list ++ (if mize_module_no_externals != "" then [] else (map getExternals module_list));
modulesFileList = let
from_mize_modules_repo = findModules mize_modules;
mize_module_no_repo = builtins.getEnv "MIZE_MODULE_NO_REPO";
in pkgs.lib.lists.flatten (from_env_var ++ (if mize_module_no_repo != "" then [] else from_mize_modules_repo) ++ (findModules self));
mize_module_path = builtins.getEnv "MIZE_MODULE_PATH";
dirs_from_path = pkgs.lib.lists.remove "" (pkgs.lib.strings.splitString ":" mize_module_path);
dirs_in_nix_store = map (path: builtins.fetchGit {
url = path;
}) dirs_from_path;
from_env_var = map findModules dirs_in_nix_store;
buildMizeForSystem = system: let
hostSystem = getCrossSystem system;
pkgsCross = import nixpkgs { inherit buildSystem hostSystem; localSystem = buildSystem; overlays = [ rust-overlay.overlays.default ]; };
pkgsNative = import nixpkgs { inherit buildSystem; overlays = [ rust-overlay.overlays.default ]; };
craneLib =
if hostSystem.kernel.name == "windows"
then
(crane.mkLib pkgs).overrideToolchain (fenix.packages.${buildSystem}.combine [
#fenix.packages.${buildSystem}.minimal.rustc
#fenix.packages.${buildSystem}.minimal.cargo
fenix.packages.${buildSystem}.nightly.toolchain
fenix.packages.${buildSystem}.targets."${hostSystem.cpu.name}-pc-windows-gnu".stable.toolchain
])
else if hostSystem.cpu.name == "wasm32"
then
(crane.mkLib pkgs).overrideToolchain (fenix.packages.${buildSystem}.combine [
fenix.packages.${buildSystem}.targets.wasm32-unknown-unknown.latest.toolchain
fenix.packages.${buildSystem}.latest.toolchain
])
else if hostSystem.name == "x86_64-linux-gnu"
then
builtins.trace "x86_64-linux-gnu hackfix...."
(crane.mkLib pkgs).overrideToolchain (fenix.packages.${buildSystem}.combine [
fenix.packages.${buildSystem}.stable.toolchain
])
else
(crane.mkLib pkgsCross).overrideToolchain (p: fenix.packages.${buildSystem}.stable.toolchain)
;
toolchain_version_drv = pkgs.stdenv.mkDerivation {
name = "toolchain_version_drv";
dontUnpack = true;
configurePhase = "";
buildPhase = ''
mkdir -p $out
touch $out/rustc-version
echo hiiiiiiiiiiiiiiiiiiiiiiii
#echo 'rustc 1.80.1 (3f5fd8dd4 2024-08-06)' > $out/rustc-version
${craneLib.cargo}/bin/rustc --version > $out/rustc-version
exit 0
'';
};
mkSelString = attrs: builtins.toJSON (attrs // {
inherit toolchain_version;
system = hostSystem.name;
mize_version = "0.0.1";
});
toolchain_version = pkgs.lib.strings.removeSuffix "\n" (builtins.readFile "${toolchain_version_drv}/rustc-version");
mkMizeModule = attrs: (if builtins.hasAttr "drvFunc" attrs then attrs.drvFunc else pkgsCross.stdenv.mkDerivation) ( rec {
name = attrs.modName;
inherit (attrs) modName;
selector_string = mkSelString (attrs.select or {} // {
inherit (attrs) modName;
});
hash = builtins.substring 0 32 (builtins.hashString "sha256" selector_string);
}
// (lib.attrsets.removeAttrs attrs ["drvFunc"] )
);
########## build a Rust Module
mkMizeRustModule = attrs: mkMizeModule (
# general stuff
{
drvFunc = craneLib.buildPackage;
MIZE_BUILD_CONFIG = mizeBuildConfigStr;
mizeInstallPhase = attrs.mizeInstallPhase or ''
mkdir -p $out/lib/
cp $build_dir/target/${hostSystem.nameRust}/$debugOrRelease/libmize_module_${attrs.modName}.so $out/lib/
'';
mizeBuildPhase = attrs.mizeBuildPhase or ''
cargo --color always build --target ${hostSystem.nameRust} ${if builtins.hasAttr "cargoExtraArgs" attrs then attrs.cargoExtraArgs else ""} --manifest-path $build_dir/Cargo.toml --lib
'';
selector_string = mkSelString (attrs.select or {} // {
inherit (attrs) modName;
});
devShell = if builtins.hasAttr "devShell" attrs then attrs.devShell else mkMizeRustShell {};
}
# linux specific stuff
// (lib.attrsets.optionalAttrs (hostSystem.kernel.name == "linux") {
# rename the so library, in case the lib.name in your Cargo.toml is not like mize_module_mylib
postInstall = (attrs.postInstall or "") + ''
if [ ! -f "$out/lib/libmize_module_${attrs.modName}" ]; then
echo RENAMING $out/lib/${attrs.modName}.so to $out/lib/libmize_module_${attrs.modName}.so
mv $out/lib/${attrs.modName}.so to $out/lib/libmize_module_${attrs.modName}.so || true
echo RENAMING $out/lib/${attrs.modName}.a to $out/lib/libmize_module_${attrs.modName}.a
mv $out/lib/${attrs.modName}.a to $out/lib/libmize_module_${attrs.modName}.a && true # ignore if there is no .a file || true
echo "$FILE"
fi
'';
})
# linux cross stuff
// (lib.attrsets.optionalAttrs (hostSystem.kernel.name == "linux" && hostSystem.isCross) {
"CARGO_TARGET_${builtins.replaceStrings ["-"] ["_"] (pkgsCross.lib.strings.toUpper hostSystem.nameFull)}_LINKER" = "${pkgsCross.stdenv.cc.targetPrefix}cc";
HOST_CC = "${pkgsCross.stdenv.cc.nativePrefix}cc";
TARGET_CC = "${pkgsCross.stdenv.cc.targetPrefix}cc";
CARGO_BUILD_TARGET = hostSystem.nameFull;
nativeBuildInputs = attrs.nativeBuildInputs or [] ++ [
pkgsCross.stdenv.cc
];
})
# wasm stuff
// (lib.attrsets.optionalAttrs (hostSystem.cpu.name == "wasm32") {
CARGO_BUILD_TARGET = "wasm32-unknown-unknown";
# checks fail on wasm
doCheck = false;
})
# add windows stuff
// (lib.attrsets.optionalAttrs (hostSystem.kernel.name == "windows") {
strictDeps = true;
doCheck = false;
CARGO_BUILD_TARGET = "${hostSystem.cpu.name}-pc-windows-gnu";
# fixes issues related to libring
TARGET_CC = "${pkgs.pkgsCross.mingwW64.stdenv.cc}/bin/${pkgs.pkgsCross.mingwW64.stdenv.cc.targetPrefix}cc";
#fixes issues related to openssl
OPENSSL_DIR = "${pkgs.openssl.dev}";
OPENSSL_LIB_DIR = "${pkgs.openssl.out}/lib";
OPENSSL_INCLUDE_DIR = "${pkgs.openssl.dev}/include/";
depsBuildBuild = [
pkgs.pkgsCross.mingwW64.stdenv.cc
pkgs.pkgsCross.mingwW64.windows.pthreads
];
})
# add the attrs passed to mkMizeRustModule
// attrs
);
buildModule = path: extraArgs: pkgs.callPackage ((import path).module or (attrs: null)) ({
inherit buildMizeForSystem mizeBuildConfig mizeBuildConfigStr;
inherit mkSelString craneLib toolchain_version;
inherit mkMizeModule mkMizeRustModule buildModule findModules hostSystem pkgsCross pkgsNative mkMizeRustShell;
inherit fenix;
mize_version = "0.0.1";
} // extraArgs );
buildLib = path: extraArgs: pkgs.callPackage ((import path).lib or (args: {})) {
inherit mkSelString craneLib toolchain_version;
inherit mkMizeModule mkMizeRustModule buildModule findModules hostSystem pkgsCross pkgsNative mkMizeRustShell;
mize_version = "0.0.1";
};
mizeBuildConfig = {
namespace = "mize.buildtime.ns";
module_url = "c2vi.dev";
selector = builtins.fromJSON (mkSelString {});
};
mizeBuildConfigStr = let
settingsFormat = pkgs.formats.toml { };
in settingsFormat.generate "mize-build-config.toml" {
config = mizeBuildConfig;
};
################# dev Shels ###################
mkMizeRustShell = attrs: mkMizeModuleShell (attrs // {
#_shell_type = "rust";
nativeBuildInputs = attrs.nativeBuildInputs or [] ++ [
pkgs.wasm-pack
pkgs.deno
pkgs.pkg-config
pkgs.wasm-bindgen-cli
(fenix.packages."x86_64-linux".combine [
fenix.packages."x86_64-linux".latest.toolchain
fenix.packages."x86_64-linux".targets.wasm32-unknown-unknown.latest.toolchain
])
# the shell script, to mk the dist folder, for a standard rust module
(pkgs.writeShellApplication {
name = "mkdist";
# should we use the folder, that cargo locate-project gives us??? or just cp ./target/debug/
text = ''
mkdir -p ./dist
mkdir -p ./dist/lib
cp ./target/debug/libmize_module*.so ./dist/lib
cp ./target/debug/libmize_module*.a ./dist/lib || true
'';
})
];
buildInputs = [
pkgsCross.openssl
];
});
mkMizeModuleShell = attrs: pkgs.mkShell (attrs // {
MIZE_BUILD_CONFIG = mizeBuildConfigStr;
IS_MIZE_DEV_SHELL = "true";
});
################# output attrset ###################
in rec {
main = modules.mize;
system = hostSystem;
modules = builtins.listToAttrs ( map ( mod: { name = mod.modName; value = mod; } ) modulesList );
# for debugging
modulesLibList = map (mod: buildLib mod {}) modulesFileList;
modulesLib = pkgs.lib.lists.foldr (a: b: b // a) {} modulesLibList;
modulesList = pkgs.lib.lists.remove null (map (mod: (buildModule mod modulesLib)) modulesFileList);
inherit toolchain_version_drv pkgsCross craneLib;
inherit mize_module_path dirs_from_path dirs_in_nix_store from_env_var;
};
mkInstallPhase = mize: ''
# install mize
mkdir -p $out/mize/${mize.system.name}
cp -r ${mize.main}/* $out/mize/${mize.system.name}/
# intall the modules
${mkModulesInstallPhase mize.modulesList}
'';
mkModulesInstallPhase = modules: pkgs.lib.concatStringsSep "\n" (map mkModuleInstallPhase modules);
mkModuleInstallPhase = module: let
hash = module.hash;
in ''
echo got module: ${module.name}
echo out: $out
echo hash: ${hash}
cp --no-preserve=mode,ownership -r ${module}/* $out/mize/dist/${hash}-${module.modName}
${pkgs.gnutar}/bin/tar -czf $out/mize/dist/${hash}-${module.modName}.tar.gz -C ${module} .
echo '${module.selector_string}' > $out/mize/dist/${hash}-${module.modName}/selector
'';
dist = systems: stdenv.mkDerivation {
name = "mize-dist";
dontUnpack = true;
# so that those binaries run on average linux-gnu systems
dontPatchShebangs = true;
buildPhase = ''
'';
installPhase = ''
mkdir -p $out
mkdir -p $out/mize
mkdir -p $out/mize/dist
'' ;# + pkgs.lib.concatStringsSep "\n" (map mkInstallPhase (map buildMizeForSystem systems));
nativeBuildInputs = [
];
};
moduleShells = system: let
mizeFor = buildMizeForSystem system;
in builtins.listToAttrs ( map ( mod: { name = mod.modName; value = (mod.devShell); } ) mizeFor.modulesList );
random = systems: rec {
distInstallPhase = map mkInstallPhase (map buildMizeForSystem systems);
mizeList = map buildMizeForSystem systems;
mize-x86_64-linux = buildMizeForSystem "x86_64-linux";
mize-x86_64-linux-installPhase = mkInstallPhase mize-x86_64-linux;
modulesListDrv = module_list_drv mize_modules;
inherit modulesFileList;
};
}

View File

@@ -1,8 +0,0 @@
export function add(a: number, b: number): number {
return a + b;
}
// Learn more at https://docs.deno.com/runtime/manual/examples/module_metadata#concepts
if (import.meta.main) {
console.log("Add 2 + 3 =", add(2, 3));
}

View File

@@ -1,6 +0,0 @@
import { assertEquals } from "@std/assert";
import { add } from "./main.ts";
Deno.test(function addTest() {
assertEquals(add(2, 3), 5);
});

View File

@@ -1,18 +0,0 @@
#!/bin/sh
#set -e
# this is a Victorinix Wrapper
source $($VIC_BINARY print-wrapper-path)
BINARY=/home/me/work/mize/target/debug/mize
function vic_build_func() {
nix develop ~/work/victorinix --impure -c cargo build --manifest-path ~/work/victorinix/Cargo.toml
}
vic_wrapper_run "$@"

View File

@@ -1,46 +0,0 @@
use crate::MizeResult;
use std::collections::HashMap;
use std::sync::{Arc, RwLock};
use crate::item::ItemData;
use crate::Mize;
type ConfigThunk = Box<dyn Fn() -> ItemData + Send + Sync>;
pub struct ConfigOpt {
pub name: String,
pub val: Option<ItemData>,
pub thunk: Option<ConfigThunk>,
}
#[derive(Clone)]
pub struct ConfigOptNameAndMize {
pub name: String,
pub mize: Mize,
}
impl ConfigOptNameAndMize {
pub fn default_val(self, val: ItemData) -> ConfigOptNameAndMize {
let mut config_opts = self.mize.config_opts.lock().unwrap();
let opt = config_opts.get_mut(&self.name).unwrap();
opt.val = Some(val);
self.clone()
}
pub fn fun(self, thunk: ConfigThunk) -> Self {
let mut config_opts = self.mize.config_opts.lock().unwrap();
let opt = config_opts.get_mut(&self.name).unwrap();
opt.thunk = Some(thunk);
self.clone()
}
}
pub fn gather_config(mize: &mut Mize) -> MizeResult<()> {
let mut config_opts = mize.config_opts.lock().unwrap();
let parts = mize.parts.lock().unwrap();
for part in parts.values() {
part.as_deref().unwrap().opts(&mut mize.clone());
}
Ok(())
}

View File

@@ -1,908 +0,0 @@
use async_trait::async_trait;
use colored::Colorize;
use core::fmt;
use flume::{bounded, unbounded, Receiver, Sender};
use interner::shared::{StringPool, VecStringPool};
use mize_macros::mize_part;
use std::any::Any;
use std::collections::HashMap;
use std::fs::create_dir;
use std::fs::File;
use std::ops::{Deref, DerefMut};
use std::path::{Path, PathBuf};
use std::sync::{Arc, Mutex};
use std::{thread, vec};
use tracing::{debug, error, info, trace, warn, Instrument};
use uuid::Uuid;
use crate::config::ConfigOptNameAndMize;
use crate::config::{gather_config, ConfigOpt};
use crate::error::{IntoMizeResult, MizeError, MizeResult, MizeResultTrait};
use crate::id::{IntoMizeId, MizeId, Namespace};
use crate::instance::store::Store;
use crate::instance::subscription::Subscription;
use crate::instance::updater::Operation;
use crate::instance::updater::{updater_thread, updater_thread_async};
use crate::item::{Item, ItemData};
use crate::memstore::MemStore;
use crate::proto::MizeMessage;
use crate::{mize_err, Module};
use self::connection::{ConnListener, Connection};
use self::updater::handle_operation;
#[cfg(feature = "async")]
use tokio::runtime::Handle;
#[cfg(feature = "async")]
use tokio::runtime::Runtime;
use core::future::Future;
use std::thread::JoinHandle;
pub mod connection;
pub mod module;
pub mod msg_thread;
pub mod store;
pub mod subscription;
pub mod updater;
#[cfg(test)]
mod tests;
static MSG_CHANNEL_SIZE: usize = 200;
static BUILD_TIME_CONFIG: &str = include_str!(std::env!("MIZE_BUILD_CONFIG"));
/// The Instance type is the heart of the mize system
#[derive(Clone)]
pub struct Mize {
// a bit a lot of Mutexes isn't it???
pub(crate) store: Arc<Mutex<Box<dyn Store>>>,
connections: Arc<Mutex<Vec<Connection>>>,
next_con_id: Arc<Mutex<u64>>,
subs: Arc<Mutex<HashMap<MizeId, Vec<Subscription>>>>,
pub(crate) modules: Arc<Mutex<HashMap<String, Box<dyn Module + Sync + Send>>>>,
pub(crate) id_pool: Arc<Mutex<VecStringPool>>,
pub(crate) namespace_pool: Arc<Mutex<StringPool>>,
pub(crate) parts: Arc<Mutex<HashMap<&'static str, Option<Box<dyn MizePart + Sync + Send>>>>>,
part_names: Arc<Mutex<Vec<&'static str>>>,
pub(crate) config_opts: Arc<Mutex<HashMap<String, ConfigOpt>>>,
// the namespace the instance operates in
pub(crate) namespace: Arc<Mutex<Namespace>>,
// the namespace of the instance itself
// TODO: set to a random uuid
pub(crate) self_namespace: Arc<Mutex<Namespace>>,
pub(crate) op_tx: Sender<Operation>,
threads: Arc<Mutex<Vec<(u32, String)>>>,
next_thread_id: Arc<Mutex<u32>>,
give_msg_wait: Arc<Mutex<HashMap<MizeId, Vec<Sender<ItemData>>>>>,
create_msg_wait: Arc<Mutex<Option<Sender<MizeId>>>>,
#[cfg(feature = "async")]
pub(crate) runtime: Arc<Mutex<Runtime>>,
}
// the public Mize API
pub struct InstanceRef {
inner: Arc<Mutex<Mize>>,
}
pub struct InstanceAsync {
inner: Arc<Mutex<Mize>>,
}
pub struct DynMizePartGuard {
pub mize: Mize,
pub part: Option<Box<dyn MizePart + Send + Sync>>,
}
pub struct MizePartGuard<T: MizePart + Send + Sync + 'static> {
pub mize: Mize,
pub part: Option<T>,
}
impl<T: MizePart + Send + Sync> Deref for MizePartGuard<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&*self.part.as_ref().unwrap()
}
}
impl<T: MizePart + Send + Sync> DerefMut for MizePartGuard<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut *self.part.as_mut().unwrap()
}
}
impl<T: MizePart + Send + Sync> Drop for MizePartGuard<T> {
fn drop(&mut self) {
if self.part.is_none() {
return;
}
let part = self.part.take().unwrap();
self.mize.give_back_part(Box::new(part));
}
}
impl Deref for DynMizePartGuard {
type Target = dyn MizePart + Send + Sync;
fn deref(&self) -> &Self::Target {
&*self.part.as_deref().unwrap()
}
}
impl DerefMut for DynMizePartGuard {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut *self.part.as_deref_mut().unwrap()
}
}
impl Drop for DynMizePartGuard {
fn drop(&mut self) {
if self.part.is_none() {
return;
}
self.mize.give_back_part(self.part.take().unwrap());
}
}
pub trait MizePartCreateGenerated: Sized {
fn create_generated(mize: Mize) -> Self;
}
pub trait MizePartGenerated {
fn name_generated(&self) -> &'static str;
fn get_mize_generated(&mut self) -> &mut Mize;
fn as_any_generated(&self) -> &dyn Any;
fn as_any_mut_generated(&mut self) -> &mut dyn Any;
fn into_any_generated(self: Box<Self>) -> Box<dyn Any>;
}
pub trait MizePartCreate: MizePartCreateGenerated + MizePart + 'static {
fn create(mize: &mut Mize) -> Box<dyn MizePart> {
Box::new(Self::create_generated(mize.clone()))
}
}
#[async_trait]
pub trait MizePart: MizePartGenerated {
fn name(&self) -> &'static str {
self.name_generated()
}
fn get_mize(&mut self) -> &mut Mize {
self.get_mize_generated()
}
fn as_any(&self) -> &dyn Any {
self.as_any_generated()
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self.as_any_mut_generated()
}
fn into_any(self: Box<Self>) -> Box<dyn Any> {
self.into_any_generated()
}
fn init(&mut self, mize: &mut Mize) -> MizeResult<()> {
Ok(())
}
fn run(&mut self, mize: &mut Mize) -> MizeResult<()> {
Ok(())
}
fn deps(&self) -> &'static [&'static str] {
&[]
}
fn opts(&self, mize: &mut Mize) {
()
}
async fn async_run(&mut self, mize: &mut Mize) -> MizeResult<()> {
Ok(())
}
async fn async_init(&mut self, mize: &mut Mize) -> MizeResult<()> {
Ok(())
}
}
impl Mize {
pub fn empty() -> MizeResult<Mize> {
let id_pool = Arc::new(Mutex::new(VecStringPool::default()));
let namespace_pool_raw = StringPool::default();
let connections = Arc::new(Mutex::new(Vec::new()));
let subs = Arc::new(Mutex::new(HashMap::new()));
let (op_tx, op_rx) = unbounded();
let give_msg_wait = Arc::new(Mutex::new(HashMap::new()));
let create_msg_wait = Arc::new(Mutex::new(None));
let namespace = Arc::new(Mutex::new(Namespace(
namespace_pool_raw.get("mize.default.namespace"),
)));
let self_namespace = Arc::new(Mutex::new(Namespace(
namespace_pool_raw.get("mize.default.namespace"),
)));
let mut instance = Mize {
store: Arc::new(Mutex::new(Box::new(MemStore::new()))),
parts: Arc::new(Mutex::new(HashMap::new())),
part_names: Arc::new(Mutex::new(Vec::new())),
config_opts: Arc::new(Mutex::new(HashMap::new())),
connections,
subs,
id_pool,
namespace,
self_namespace,
op_tx,
namespace_pool: Arc::new(Mutex::new(namespace_pool_raw)),
modules: Arc::new(Mutex::new(HashMap::new())),
threads: Arc::new(Mutex::new(Vec::new())),
next_thread_id: Arc::new(Mutex::new(0)),
next_con_id: Arc::new(Mutex::new(1)),
give_msg_wait,
create_msg_wait,
#[cfg(feature = "async")]
runtime: Arc::new(Mutex::new(
Runtime::new().mize_result_msg("Could not create async runtime")?,
)),
};
#[cfg(feature = "target-os")]
{
let instance_clone = instance.clone();
let op_rx_clone = op_rx.clone();
let closure = move || updater_thread(op_rx_clone, &instance_clone);
instance.spawn("updater_thread", closure)?;
let instance_clone_two = instance.clone();
let closure_two = move || updater_thread(op_rx, &instance_clone_two);
instance.spawn("updater_thread", closure_two)?;
}
// set up async update "threads" when using wasm
#[cfg(feature = "target-wasm ")]
{
let instance_clone = instance.clone();
let op_rx_clone = op_rx.clone();
wasm_bindgen_futures::spawn_local(updater_thread_async(op_rx_clone, instance_clone));
let instance_clone_two = instance.clone();
wasm_bindgen_futures::spawn_local(updater_thread_async(op_rx, instance_clone_two));
}
// will move the msg stuff into it's own thread
// like this it can deadlock.... if a msg waits on an operation to complete
//let msg_instance_clone = instance.clone();
//let msg_closure = move || msg_thread(op_rx, instance_clone);
//instance.spawn("msg_thread", closure)?;
#[cfg(feature = "target-wasm ")]
console_log!("before loading build time config");
// load the config from build time
let config = ItemData::from_toml(BUILD_TIME_CONFIG)?;
instance.set_blocking("0", config);
#[cfg(feature = "target-wasm ")]
console_log!("after loading build time config");
return Ok(instance);
}
pub fn new() -> MizeResult<Mize> {
trace!("[ {} ] Instance::new()", "CALL".yellow());
let mut instance = Mize::empty()?;
instance.init();
debug!(
"instance inited with config: {}",
instance.get("0/config")?.as_data_full()?
);
return Ok(instance);
}
pub fn get_part(&mut self, name: &str) -> MizeResult<DynMizePartGuard> {
match self.parts.lock().unwrap().get_mut(name) {
Some(part) => Ok(DynMizePartGuard {
part: Some(part.take().unwrap()),
mize: self.clone(),
}),
None => Err(mize_err!("Part not found or currently taken")),
}
}
pub fn add_part(&mut self, part: Box<dyn MizePart + Send + Sync>) -> MizeResult<()> {
self.part_names.lock().unwrap().push(part.name());
self.parts.lock().unwrap().insert(part.name(), Some(part));
Ok(())
}
pub fn register_part(&mut self, part: Box<dyn MizePart + Send + Sync>) -> MizeResult<()> {
self.part_names.lock().unwrap().push(part.name());
self.parts.lock().unwrap().insert(part.name(), Some(part));
Ok(())
}
fn part_names(&mut self) -> Vec<&'static str> {
self.part_names.lock().unwrap().clone()
}
pub(crate) fn give_back_part(&mut self, part: Box<dyn MizePart + Send + Sync>) {
self.parts.lock().unwrap().insert(part.name(), Some(part));
}
pub fn init(&mut self) -> MizeResult<()> {
// gather options
gather_config(self)?;
// platform specific init code
crate::platform::any::instance_init(self)?;
// end of platform specific init code
// load the modules, ad specified in the load_modules config
match self.get("0/config/load_modules")?.value_string() {
Ok(modules_to_load) => {
for module in modules_to_load.split(" ") {
#[cfg(feature = "target-wasm ")]
console_log!("loading module in Instance::init() ... {}", module);
self.load_module(module)?;
}
}
Err(err) => {
// no load_modules option is set
}
}
debug!("INSTANCE INIT DONE");
Ok(())
}
pub fn with_config(config: ItemData) -> MizeResult<Mize> {
trace!("[ {} ] Instance::with_config()", "CALL".yellow());
trace!("config: {}", config);
let mut instance = Mize::empty()?;
instance.set_blocking("0", config);
instance.init()?;
Ok(instance)
}
pub fn migrate_to_store(&self, new_store: Box<dyn Store>) -> MizeResult<()> {
info!("MIGRATING");
let mut old_store = self.store.lock()?;
let id = self.id_from_string("0".to_owned())?;
let inst_data = old_store.get_value_data_full(id.clone())?;
new_store.set(id, inst_data.to_owned())?;
for id in old_store.id_iter()? {
let data = old_store.get_value_data_full(self.id_from_string(id?)?)?;
let id_of_new_store = new_store.new_id()?;
new_store.set(self.id_from_string(id_of_new_store)?, data.to_owned())?;
}
*old_store = new_store;
Ok(())
}
pub fn new_item(&self) -> MizeResult<Item> {
if self.get_namespace()? != self.get_self_namespace()? {
// need to send create msg and wait for it
let mut connection = self.get_connection_by_ns(self.get_namespace()?)?;
let msg = MizeMessage::new_create(connection.id);
connection.send(msg)?;
let (tx, rx) = bounded::<MizeId>(1);
let mut msg_wait_inner = self.create_msg_wait.lock()?;
*msg_wait_inner = Some(tx);
drop(msg_wait_inner);
let id = rx.recv()?;
println!("new_item namespace: {:?}", id.namespace());
return Ok(Item::new(id, self));
}
let store_inner = self.store.lock()?;
let id = self.id_from_string(store_inner.new_id()?)?;
return Ok(Item::new(id, self));
}
pub fn get<I: IntoMizeId>(&self, id: I) -> MizeResult<Item> {
let id = id.to_mize_id(self)?;
return Ok(Item::new(id, &self));
}
pub fn set<I: IntoMizeId, V: Into<ItemData>>(&self, id: I, value: V) -> MizeResult<()> {
let id = id.to_mize_id(self)?;
self.op_tx.send(Operation::Set(id, value.into(), None));
Ok(())
}
pub fn set_blocking<I: IntoMizeId, V: Into<ItemData>>(
&self,
id: I,
value: V,
) -> MizeResult<()> {
handle_operation(
&mut Operation::Set(id.to_mize_id(self)?, value.into(), None),
self,
)?;
Ok(())
}
pub fn sub<I: IntoMizeId>(&self, id: I, sub: Subscription) -> MizeResult<()> {
let mut subs_inner = self.subs.lock()?;
let id = id.to_mize_id(self)?;
match subs_inner.get_mut(&id) {
Some(vec) => {
vec.push(sub);
}
None => {
subs_inner.insert(id.clone(), vec![sub]);
}
}
// if we are not the owner of this item, send a sub msg to them
if !self.we_are_namespace()? {
let con = self.get_connection_by_ns(id.namespace())?;
let msg = MizeMessage::new_sub(id, con.id);
con.send(msg)?;
}
Ok(())
}
pub fn new_id<T: IntoMizeId>(&self, value: T) -> MizeResult<MizeId> {
value.to_mize_id(self)
}
pub fn id_from_string(&self, string: String) -> MizeResult<MizeId> {
let vec_string: Vec<String> = string
.split("/")
.map(|v| v.to_owned())
.filter(|el| el.as_str() != "")
.collect();
self.id_from_vec_string(vec_string)
}
pub fn id_from_vec_string(&self, mut vec_string: Vec<String>) -> MizeResult<MizeId> {
let id_pool_inner = self.id_pool.lock()?;
let namespace_inner = self.namespace.lock()?;
let first_el = vec_string
.first_mut()
.ok_or(mize_err!("MizeId was empty"))?;
let id = if first_el.contains(":") {
// first el is a namespace + store_part
let new_first_el = first_el.clone();
let vec: Vec<&str> = new_first_el.split(":").collect();
let ns_part = vec
.iter()
.nth(0)
.ok_or(mize_err!("should really not happen"))?;
let store_part = vec.iter().nth(1).ok_or(mize_err!(
"mizeid was like 'namespace:/hi', why are you doing that"
))?;
*first_el = store_part.to_owned().to_owned();
MizeId {
path: id_pool_inner.get(vec_string),
namespace: self.namespace_from_string(ns_part.to_owned().to_owned())?,
}
} else {
MizeId {
path: id_pool_inner.get(vec_string),
namespace: namespace_inner.clone(),
}
};
trace!("new MizeId made: {:?}", id);
Ok(id)
}
pub fn load_module(&mut self, name: &str) -> MizeResult<()> {
crate::platform::any::load_module(self, name, None)
}
pub fn fetch_module(&mut self, name: &str) -> MizeResult<String> {
// platform specific way to fetch a module
crate::platform::any::fetch_module(self, name)
}
pub fn load_module_at(&mut self, name: &str, path: String) -> MizeResult<()> {
// platform specific init code
crate::platform::any::load_module(self, name, Some(path))
}
pub fn get_module(&mut self, name: &str) -> MizeResult<Box<dyn Module + Send + Sync>> {
let inner = self.modules.lock()?;
let module = inner
.get(name)
.ok_or(mize_err!("Couldn't get_module('{name}')"))?
.clone_module();
Ok(module)
}
pub fn namespace_from_string(&self, ns_str: String) -> MizeResult<Namespace> {
let namespace_pool_inner = self.namespace_pool.lock()?;
let namespace = Namespace(namespace_pool_inner.get(ns_str));
Ok(namespace)
}
pub fn set_namespace(&self, ns: Namespace) -> MizeResult<()> {
let mut namespace_inner = self.namespace.lock()?;
*namespace_inner = ns;
Ok(())
}
pub fn get_namespace(&self) -> MizeResult<Namespace> {
let mut namespace_inner = self.namespace.lock()?;
return Ok(namespace_inner.clone());
}
pub fn get_self_namespace(&self) -> MizeResult<Namespace> {
let mut self_namespace_inner = self.self_namespace.lock()?;
return Ok(self_namespace_inner.clone());
}
pub fn we_are_namespace(&self) -> MizeResult<bool> {
Ok(self.get_namespace()? == self.get_self_namespace()?)
}
pub fn add_listener<T: ConnListener + 'static>(&mut self, listener: T) -> MizeResult<()> {
let mut instance_clone = self.clone();
self.spawn("some_listener", move || listener.listen(instance_clone));
Ok(())
}
pub fn new_connection(&self, tx: Sender<MizeMessage>) -> MizeResult<u64> {
let mut conn_inner = self.connections.lock()?;
let mut next_con_id = self.next_con_id.lock()?;
let old_next_con_id = *next_con_id;
let connection = Connection {
id: next_con_id.to_owned(),
tx,
ns: None,
};
conn_inner.push(connection);
*next_con_id += 1;
Ok(old_next_con_id)
}
pub fn new_connection_join_namespace(&self, tx: Sender<MizeMessage>) -> MizeResult<u64> {
let conn_id = self.new_connection(tx)?;
let ns_of_peer_str = self
.get(format!(
"inst/con_by_id/{}/peer/0/config/namespace",
conn_id
))?
.value_string()?;
let ns_of_peer = self.namespace_from_string(ns_of_peer_str)?;
self.connection_set_namespace(conn_id, ns_of_peer.clone());
self.set_namespace(ns_of_peer);
Ok(conn_id)
}
pub fn connection_set_namespace(&self, conn_id: u64, namespace: Namespace) -> MizeResult<()> {
let mut connection = self.get_connection(conn_id)?;
connection.ns = Some(namespace);
self.set_connection(conn_id, connection);
Ok(())
}
pub fn got_msg(&self, msg: MizeMessage) -> MizeResult<()> {
Ok(self.op_tx.send(Operation::Msg(msg))?)
}
pub fn report_err(&self, err: MizeError) {
err.log();
}
fn set_connection(&self, conn_id: u64, new_connection: Connection) -> MizeResult<()> {
let mut conn_inner = self.connections.lock()?;
for connection in conn_inner.iter_mut() {
if connection.id == conn_id {
*connection = new_connection;
return Ok(());
}
}
return Err(mize_err!(
"Connection with id {} not known to instance",
conn_id
));
Ok(())
}
pub fn get_connection(&self, conn_id: u64) -> MizeResult<Connection> {
let mut conn_inner = self.connections.lock()?;
for connection in conn_inner.iter() {
if connection.id == conn_id {
return Ok(connection.clone());
}
}
return Err(mize_err!(
"Connection with id {} not known to instance",
conn_id
));
}
pub fn get_connection_by_ns(&self, ns: Namespace) -> MizeResult<Connection> {
let mut conn_inner = self.connections.lock()?;
for connection in conn_inner.iter() {
if connection.ns == Some(ns.clone()) {
return Ok(connection.clone());
}
}
return Err(mize_err!(
"Connection with namespace {} not known to instance",
ns.as_string()
));
}
pub fn spawn(
&mut self,
name: &str,
func: impl FnOnce() -> MizeResult<()> + Send + 'static,
) -> MizeResult<()> {
let mut threads_inner = self.threads.lock()?;
let mut next_thread_id = self.next_thread_id.lock()?;
threads_inner.push((*next_thread_id, name.to_owned()));
let my_thread_id_no_mutex_guard = *next_thread_id;
let thread_mutex = self.threads.clone();
let name_to_move = name.to_owned();
let to_spawn = move || -> MizeResult<()> {
debug!("spawning thread: {}", name_to_move);
let my_thread_id = my_thread_id_no_mutex_guard;
func()?;
let mut threads_inner = thread_mutex.lock()?;
*threads_inner = threads_inner
.clone()
.into_iter()
.filter(|el| match el {
(my_thread_id, _) => false,
(_, _) => true,
})
.collect();
debug!("thread '{}' stopped", name_to_move);
Ok(())
};
*next_thread_id += 1;
#[cfg(feature = "target-os")]
thread::spawn(move || to_spawn());
#[cfg(feature = "target-wasm ")]
{
//console_log!("in instance::spawn with wasm target")
}
//NOT WELL SUPPORTED
//crate::platform::wasm::wasm_spawn(to_spawn)?;
Ok(())
}
pub fn give_msg_wait(&self, id: MizeId) -> MizeResult<ItemData> {
let mut give_msg_wait_inner = self.give_msg_wait.lock()?;
let (tx, rx) = bounded::<ItemData>(1);
let vec = match give_msg_wait_inner.get_mut(&id) {
Some(vec) => vec,
None => {
give_msg_wait_inner.insert(id.clone(), Vec::new());
give_msg_wait_inner.get_mut(&id).unwrap()
}
};
vec.push(tx);
// so that another thread can also give_msg_wait(), while we wait in the recv() of rx
drop(vec);
drop(give_msg_wait_inner);
let data = rx.recv()?;
return Ok(data);
}
#[cfg(feature = "async")]
pub fn spawn_async<F: Future<Output = impl Send + 'static> + Send + 'static>(
&mut self,
name: &str,
func: F,
) -> MizeResult<()> {
let mut threads_inner = self.threads.lock()?;
let mut next_thread_id = self.next_thread_id.lock()?;
threads_inner.push((*next_thread_id, name.to_owned()));
let runtime_inner = self.runtime.lock().unwrap();
runtime_inner.spawn(func);
*next_thread_id += 1;
Ok(())
}
#[cfg(feature = "async")]
pub fn async_get_handle(&self) -> Handle {
let runtime_inner = self.runtime.lock().unwrap();
let handle = runtime_inner.handle().to_owned();
handle
}
#[cfg(feature = "async")]
pub fn spawn_async_blocking<
F: Future<Output = impl Send + Sync + 'static> + Send + Sync + 'static,
>(
&mut self,
name: &str,
func: F,
) -> F::Output {
let mut threads_inner = self
.threads
.lock()
.expect("mutex lock failed in spawn_async_blocking");
let mut next_thread_id = self
.next_thread_id
.lock()
.expect("mutex lock failed in spawn_async_blocking");
threads_inner.push((*next_thread_id, name.to_owned()));
let runtime_inner = self.runtime.lock().unwrap();
let handle = runtime_inner.handle().to_owned();
*next_thread_id += 1;
drop(runtime_inner);
drop(threads_inner);
drop(next_thread_id);
let result = handle.block_on(func);
return result;
}
pub fn wait(&self) {
info!("Instance main thread waiting");
loop {
thread::sleep_ms(10000000)
}
}
pub fn report_error(err: MizeError) {
err.log();
}
pub fn get_config(&mut self, name: &str) -> MizeResult<ItemData> {
let mut config_opts = self.config_opts.lock().unwrap();
let opt = config_opts
.get_mut(name)
.ok_or_else(|| mize_err!("Config opt for {name} not in config_opts"))?;
// get the cached value
if let Some(val) = opt.val.clone() {
return Ok(val);
}
// Evaluate the thunk
let thunk = opt
.thunk
.take()
.ok_or_else(|| mize_err!("ConfigOpt {name} does not have a thunk"))?;
let result = thunk();
// Cache the result
opt.val = Some(result.clone());
return Ok(result);
}
pub fn new_opt(&mut self, name: &str) -> ConfigOptNameAndMize {
let mut config_opts = self.config_opts.lock().unwrap();
let opt = ConfigOpt {
name: name.to_owned(),
val: None,
thunk: None,
};
config_opts.insert(name.to_string(), opt);
ConfigOptNameAndMize {
name: name.to_string(),
mize: self.clone(),
}
}
pub fn run(&mut self) -> MizeResult<()> {
let mut parts = self.parts.lock().unwrap();
for part in parts.values_mut() {
part.as_deref_mut().unwrap().run(&mut self.clone())?;
}
Ok(())
}
pub fn get_part_native<T: MizePart + Send + Sync + 'static>(
&mut self,
name: &str,
) -> MizeResult<MizePartGuard<T>> {
let mut dyn_guard = self.get_part(name)?;
let part = dyn_guard.part.take().unwrap();
let concrete_part = part.into_any().downcast::<T>().unwrap();
Ok(MizePartGuard {
mize: self.clone(),
part: Some(*concrete_part),
})
}
pub fn add_name_only_part(&mut self, name: &'static str) {
let part = NameOnlyPart {
mize: self.clone(),
name,
};
self.add_part(Box::new(part));
}
}
#[derive(Default)]
struct NameOnlyPart {
mize: Mize,
name: &'static str,
}
impl MizePart for NameOnlyPart {}
impl MizePartGenerated for NameOnlyPart {
fn as_any_generated(&self) -> &dyn Any {
self.as_any()
}
fn as_any_mut_generated(&mut self) -> &mut dyn Any {
self.as_any_mut()
}
fn get_mize_generated(&mut self) -> &mut Mize {
&mut self.mize
}
fn into_any_generated(self: Box<Self>) -> Box<dyn Any> {
self.into_any()
}
fn name_generated(&self) -> &'static str {
self.name
}
}
impl fmt::Debug for Mize {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Mize Instance with subs: {:?}", self.subs,)
}
}
impl Default for Mize {
fn default() -> Self {
Self::empty().unwrap()
}
}

View File

@@ -1,60 +0,0 @@
// https://users.rust-lang.org/t/casting-between-trait-object-types/97220/2
use std::ffi::OsString;
use crate::error::MizeResult;
use crate::instance::Mize;
pub trait Module {
fn init(&mut self, instance: &Mize) -> MizeResult<()>;
fn exit(&mut self, instance: &Mize) -> MizeResult<()>;
fn clone_module(&self) -> Box<dyn Module + Send + Sync>;
// extra traits, that can be implemented
// return None if not implemented
fn run_cli(&mut self, instance: &Mize, cmd_line: Vec<OsString>) -> Option<MizeResult<()>> {
None
}
}
// console_log macro
// that can be copied into other files for debugging purposes
#[cfg(feature = "target-wasm ")]
use wasm_bindgen::prelude::*;
#[cfg(feature = "target-wasm ")]
#[wasm_bindgen]
extern "C" {
#[wasm_bindgen(js_namespace = console)]
fn log(s: &str);
}
#[cfg(feature = "wasm-target")]
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())})
}
pub struct EmptyModule {}
impl Module for EmptyModule {
fn init(&mut self, instance: &Mize) -> MizeResult<()> {
println!("empty module fn init");
#[cfg(feature = "wasm-target")]
console_log!("empty module fn init");
Ok(())
}
fn exit(&mut self, instance: &Mize) -> MizeResult<()> {
println!("empty module fn exit");
Ok(())
}
fn clone_module(&self) -> Box<dyn Module + Send + Sync> {
Box::new(EmptyModule {})
}
}

View File

@@ -1,714 +0,0 @@
use colored::Colorize;
use core::fmt;
use serde::Deserialize;
use serde::Serialize;
use std::fmt::Debug;
use std::fs::File;
use std::io;
use std::rc::Rc;
use std::{collections::HashMap, fmt::Display, io::Cursor, path::PathBuf};
use std::{i128, string};
use tracing::{debug, error, info, instrument, trace, warn, Instrument};
use tracing::{span, Level};
use crate::error::{IntoMizeResult, MizeError, MizeResult};
use crate::id::MizeId;
use crate::instance::connection::value_raw_con_by_id;
use crate::instance::store::Store;
use crate::instance::{connection, Mize};
use crate::mize_err;
use crate::proto::MizeMessage;
use ciborium::Value as CborValue;
// a item always has to do with a Instance, which takes care of how it is updated
#[derive(Debug, Clone)]
pub struct Item<'a> {
id: MizeId,
pub instance: &'a Mize,
}
// without an Instance it is not an item, but only the "data of an item"
// and this type for now is just an alias to CborValue
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ItemData(pub CborValue);
impl Item<'_> {
pub fn id(&self) -> MizeId {
self.id.clone()
}
pub fn new(id: MizeId, instance: &Mize) -> Item {
Item { id, instance }
}
pub fn value_raw(&self) -> MizeResult<Vec<u8>> {
let data = self.as_data_full()?;
let raw = get_raw_from_cbor(data.cbor(), vec![])?;
return Ok(raw.to_owned());
}
pub fn value_string(&self) -> MizeResult<String> {
let raw = self.value_raw()?;
String::from_utf8(raw.clone())
.mize_result_msg(format!("failed to convert raw value from item '{}' into a utf-8 string. raw value was: '{:02X?}'", self.id(), raw))
}
pub fn as_data_full(&self) -> MizeResult<ItemData> {
let id = self.id();
// the instance we are
if id.store_part() == "self" {
debug!("getting item '{}' from self", self.id());
match id.nth_part(1)? {
"con_by_id" => {
return value_raw_con_by_id(&mut self.clone());
}
"namespace" => {
let namespace_inner = self.instance.namespace.lock()?;
return Ok(ItemData::from_string(namespace_inner.as_real_string()));
}
"self_namespace" => {
let namespace_inner = self.instance.self_namespace.lock()?;
return Ok(ItemData::from_string(namespace_inner.as_real_string()));
}
"config" => {
let rest_path = id
.path
.into_iter()
.skip(2)
.map(|v| v.to_owned())
.collect::<Vec<String>>()
.join("/");
let id_for_store = self
.instance
.new_id("0/config".to_owned() + "/" + rest_path.as_str())?;
let store = self.instance.store.lock()?;
let data = store.get_value_data_full(id_for_store)?;
return Ok(data);
}
_ => {}
}
return Err(mize_err!(
"a /self path, but the next element in the path '{:?}' is not valid",
id.nth_part(1)
));
}
let self_namespace_inner = self.instance.self_namespace.lock()?;
if self.id().namespace() == self_namespace_inner.to_owned() {
debug!("getting item '{}' from store", self.id());
if id.store_part() == "inst" {
match id.nth_part(1)? {
"con_by_id" => {
return value_raw_con_by_id(&mut self.clone());
}
"namespace" => {
let namespace_inner = self.instance.namespace.lock()?;
return Ok(ItemData::from_string(namespace_inner.as_real_string()));
}
"self_namespace" => {
let namespace_inner = self.instance.self_namespace.lock()?;
return Ok(ItemData::from_string(namespace_inner.as_real_string()));
}
_ => {}
}
}
let store_inner = self.instance.store.lock()?;
return store_inner.get_value_data_full(self.id());
} else {
let mut connection = self.instance.get_connection_by_ns(self.id().namespace())?;
debug!(
"getting item '{}' from connection '{}' from store",
self.id(),
connection.id
);
let msg = MizeMessage::new_get(self.id(), connection.id);
connection.send(msg);
let data = self.instance.give_msg_wait(self.id())?;
return Ok(data);
}
}
#[instrument(name = "fn.ItemData::merge")]
pub fn merge<V: Into<ItemData> + Debug>(&mut self, mut value: V) -> MizeResult<()> {
let mut data = self.as_data_full()?;
//let mut data = data_full.get_path(id_path_without_store_part)?;
trace!("item::merge data: {:?}", data);
trace!("item::merge id: {:?}", self.id());
let new_data = value.into();
data.merge(new_data);
trace!("item::merge new_data: {:?}", data);
// handle the case of /self/*
if self.id().store_part() == "self" {
let rest_path = self
.id()
.path
.into_iter()
.skip(1)
.map(|v| v.to_owned())
.collect::<Vec<String>>()
.join("/");
let id_for_store = self
.instance
.new_id("0".to_owned() + "/" + rest_path.as_str())?;
let store_inner = self.instance.store.lock()?;
store_inner.set(id_for_store, data)?;
} else if self.instance.we_are_namespace()? {
let store_inner = self.instance.store.lock()?;
store_inner.set(self.id(), data)?;
} else {
let namespace = self.id().namespace();
let mut connection = self.instance.get_connection_by_ns(namespace)?;
let msg = MizeMessage::new_update_request(self.id(), data, connection.id);
connection.send(msg)?;
}
Ok(())
}
}
impl<'a> Display for Item<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
//let item_data = self.as_data_full().map_err(|_| std::fmt::Error)?;
write!(f, "Item with id: {}", self.id());
return Ok(());
}
}
impl ItemData {
pub fn new() -> ItemData {
ItemData(CborValue::Null)
}
pub fn empty() -> ItemData {
ItemData(CborValue::Null)
}
pub fn from_toml(toml_string: &str) -> MizeResult<ItemData> {
let toml_deserializer = toml::Deserializer::new(&toml_string);
let value = CborValue::deserialize(toml_deserializer).mize_result_msg(format!(
"Could not deserialize the toml string: {}",
&toml_string
))?;
return Ok(value.into_item_data());
}
pub fn value_string(self) -> MizeResult<String> {
match self.cbor() {
CborValue::Text(string) => Ok(string.to_owned()),
_ => Err(mize_err!("this ItemData has no string value")),
}
}
pub fn from_string<S: Into<String>>(into_string: S) -> ItemData {
let string = into_string.into();
let data = CborValue::Text(string);
return ItemData(data);
}
pub fn to_json(self) -> MizeResult<String> {
let mut result: Vec<u8> = Vec::new();
self.0
.serialize(&mut serde_json::Serializer::new(&mut result))?;
Ok(String::from_utf8(result)?)
}
pub fn from_json(json_str: String) -> MizeResult<ItemData> {
let mut json_deserializer = serde_json::Deserializer::from_str(json_str.as_str());
let value = CborValue::deserialize(&mut json_deserializer).mize_result_msg(format!(
"could not deserialize the json string: {}",
json_str
))?;
return Ok(value.into_item_data());
}
pub fn merge(&mut self, other: ItemData) {
item_data_merge(&mut self.0, &other.0);
}
pub fn cbor(&self) -> &CborValue {
&self.0
}
pub fn from_cbor(cbor: CborValue) -> ItemData {
ItemData(cbor)
}
pub fn sort_keys(&mut self) -> MizeResult<()> {
item_data_sort_keys(&mut self.0)
}
pub fn null() -> CborValue {
CborValue::Null
}
pub fn parse<T: Into<String>>(value: T) -> ItemData {
let value_str = value.into();
if value_str == "false" {
return CborValue::Bool(false).into_item_data();
}
if value_str == "false" {
return CborValue::Bool(true).into_item_data();
}
if let Ok(int) = value_str.parse::<i128>() {
return int.into_item_data();
}
return CborValue::Text(value_str).into_item_data();
}
pub fn set_path<P: IntoPath, D: IntoItemData>(&mut self, path: P, value: D) -> MizeResult<()> {
//trace!("[ {} ] ItemData::set_path()", "CALL".yellow());
let path = path.into_path();
let value = value.into_item_data();
item_data_set_path(&mut self.0, path, &value.0)
}
pub fn get_path<P: IntoPath>(&self, path: P) -> MizeResult<ItemData> {
let path = path.into_path();
Ok(item_data_get_path(&self.0, path)?
.to_owned()
.into_item_data())
}
}
impl Default for ItemData {
fn default() -> Self {
ItemData::new()
}
}
pub fn data_from_string(data_string: String) -> MizeResult<ItemData> {
let mut data = ItemData::new();
// in case it's just a string
if !data_string.contains("=") {
let cbor = CborValue::Text(data_string);
return Ok(ItemData::from_cbor(cbor));
}
for option in data_string.split(":") {
//trace!("data_from_string: option: {}", option);
if option == "".to_owned() {
continue;
}
let path = option
.split("=")
.nth(0)
.ok_or(MizeError::new().msg(format!(
"Failed to parse Option: option '{}' has an empty path (thing before =)",
option
)))?;
//trace!("data_from_string: path: {}", path);
let value = option
.split("=")
.nth(1)
.ok_or(MizeError::new().msg(format!(
"Failed to parse Option: option '{}' has an empty value (thing after =)",
option
)))?;
//trace!("data_from_string: value: {}", value);
let mut path_vec = Vec::new();
path_vec.extend(path.split("."));
//trace!("data_from_string: path_vec: {:?}", path_vec);
data.set_path(path_vec, ItemData::parse(value))?;
}
trace!("data_from_string: data: {}", data);
return Ok(data);
}
pub fn item_data_get_path(data: &CborValue, path: Vec<String>) -> MizeResult<&CborValue> {
let mut path_iter = path.clone().into_iter();
//println!("data: {:?}", data);
//println!("path: {:?}", path);
let path_el = match path_iter.nth(0) {
Some(val) => val,
None => return Ok(data), // our base case
};
let mut sub_data = &CborValue::Null;
match data {
CborValue::Map(ref map) => {
for (key, val) in map {
//println!("key: {:?}", key);
//println!("path_el: {}", path_el);
if let CborValue::Text(key_str) = key {
if key_str == &path_el {
sub_data = val;
}
}
}
}
val => {
return Err(MizeError::new()
.msg(format!(
"Failed to get path '{:?}' from ItemData, the data at '{}' is not a map",
path, path_el
))
.msg(format!("{:?} is: {:?}", path_el, val)));
}
};
item_data_get_path(sub_data, path_iter.collect())
}
pub fn item_data_sort_keys(data: &mut CborValue) -> MizeResult<()> {
// for a list, sort each item of the list
if data.is_array() {
for el in data.as_array_mut().unwrap().iter_mut() {
item_data_sort_keys(el)?
}
}
// sort the keys of a map
if data.is_map() {
let map: &mut Vec<(CborValue, CborValue)> = data.as_map_mut().unwrap();
//map.sort_by_key(|el| el.0);
map.sort_by(|a, b| {
match a.0.partial_cmp(&b.0) {
Some(val) => val,
None => {
// just say the two values are equal, if they can't be ordered
std::cmp::Ordering::Equal
}
}
});
// also sort every sub value
for (key, val) in map {
item_data_sort_keys(val)?
}
}
Ok(())
}
pub fn item_data_set_path(
data: &mut CborValue,
path: Vec<String>,
data_to_set: &CborValue,
) -> MizeResult<()> {
//trace!("[ {} ] item_data_set_path()", "CALL".yellow());
//trace!("[ {} ] data: {}", "ARG".yellow(), data.clone().into_item_data());
//trace!("[ {} ] path: {:?}", "ARG".yellow(), path);
//trace!("[ {} ] data_to_set: {}", "ARG".yellow(), data_to_set.clone().into_item_data());
test_println!("item_data_set_path ###########################");
test_println!("data: {}", ItemData::from_cbor(data.clone()));
test_println!("path: {:?}", path);
test_println!("data_to_set: {}", ItemData::from_cbor(data_to_set.clone()));
test_println!("###########################");
let mut path_iter = path.clone().into_iter();
let path_el = match path_iter.nth(0) {
Some(val) => val,
None => {
//trace!("item_data_set_path: base_case");
// our base case
*data = data_to_set.to_owned();
return Ok(());
}
};
match data {
CborValue::Map(ref mut map) => {
for (key, val) in &mut *map {
if let CborValue::Text(key_str) = key {
if key_str == &path_el {
//trace!("item_data_set_path: setting key {key_str} in map with recursive call");
return item_data_set_path(val, path_iter.collect(), data_to_set);
}
}
}
// if we get here, that means, the key (which is the variable path_el) does not exist
// in the map
// so add it... but also with a recursive call which is needed, in case path_iter
// longer than 1
//trace!("item_data_set_path: adding key {path_el} to map");
let mut inner_value = CborValue::Null;
item_data_set_path(&mut inner_value, path_iter.collect(), data_to_set);
map.push((CborValue::Text(path_el.clone()), inner_value));
return Ok(());
}
CborValue::Null => {
//trace!("item_data_set_path: CborValue::Null case");
let mut inner_value = CborValue::Null;
item_data_set_path(&mut inner_value, path_iter.collect(), data_to_set);
let map_vec = vec![(CborValue::Text(path_el), inner_value)];
*data = CborValue::Map(map_vec);
return Ok(());
}
val => {
return Err(MizeError::new()
.msg(format!(
"Failed to get path '{:?}' from ItemData, the data at '{}' is not a map",
path, path_el
))
.msg(format!("{:?} is: {:?}", path_el, val)));
}
};
return Err(MizeError::new().msg("unreachable"));
}
pub fn item_data_merge(merge_into: &mut CborValue, other: &CborValue) {
// needs to be recursive
match (merge_into, other) {
// if both are maps
// merge the keys/vals in the maps and call recursively on them
(CborValue::Map(ref mut merge_into_map), CborValue::Map(ref other_map)) => {
// go through other_map and merge those keys/values to merge_into
for (other_key, other_val) in other_map {
// so find that same vale in merge_into, if exists recursively merge, else just add to
// vec
let mut found_value = CborValue::Null;
let mut value_found = false;
let mut new_value_for_merge_into: Vec<(CborValue, CborValue)> = Vec::new(); // here we collect the
// new contents of the
// map, to assign later
// to merge_into_map
for (merge_into_key, merge_into_val) in merge_into_map.clone() {
if merge_into_key == *other_key {
value_found = true;
found_value = merge_into_val.to_owned();
// if we found it, also don't add it to new_value_for_merge_into
} else {
//if not found push to new_value_for_merge_into
new_value_for_merge_into
.push((merge_into_key.to_owned(), merge_into_val.to_owned()));
}
}
if value_found {
// we found it so merge and asign
item_data_merge(&mut found_value, other_val);
// to asign push to new_value_for_merge_into, because the old was removed already
new_value_for_merge_into.push((other_key.to_owned(), found_value));
} else {
// if not found, just add the other_key/val to new_value_for_merge_into
new_value_for_merge_into.push((other_key.to_owned(), other_val.to_owned()));
}
// finally, we need to asign the new_value_for_merge_into into merge_into
*merge_into_map = new_value_for_merge_into;
}
}
// if other is Null, don't asign
// this is gonna have repercussions.... because it's not clean behaviour
(merge_into, CborValue::Null) => {}
// in any other case we just want to set merge_into to other
// also this is the base case
(merge_into, other) => {
*merge_into = other.to_owned();
}
}
}
#[instrument(name = "fn.get_raw_from_cbor")]
pub fn get_raw_from_cbor<'a>(value: &'a CborValue, path: Vec<&String>) -> MizeResult<&'a [u8]> {
trace!("[ {} ] value: {:?}", "ARG".yellow(), value);
trace!("[ {} ] path: {:?}", "ARG".yellow(), path);
let mut path_iter = path.clone().into_iter();
let path_el = match path_iter.nth(0) {
Some(val) => val,
None => {
// our base case
let ret_val = match value {
CborValue::Bytes(vec) => {
trace!("[ {} ] ret value: {:?}", "RET".yellow(), &vec[..]);
return Ok(&vec[..]);
}
CborValue::Text(string) => {
trace!("[ {} ] ret value: {:?}", "RET".yellow(), string.as_bytes());
return Ok(string.as_bytes());
}
other => {
return Err(mize_err!(
"path is empty and the value: '{:?}' is neither Bytes nor Text",
other
));
}
};
}
};
match value {
CborValue::Bytes(vec) => {
trace!("[ {} ] ret value: {:?}", "RET".yellow(), &vec[..]);
return Ok(&vec[..]);
}
CborValue::Text(string) => {
trace!("[ {} ] ret value: {:?}", "RET".yellow(), string.as_bytes());
return Ok(string.as_bytes());
}
CborValue::Map(map) => {
let mut inner_val: &CborValue = &CborValue::Null;
let mut inner_val_found = false;
// find the inner_val at path
for (key, val) in map.into_iter() {
if let CborValue::Text(key_text) = key {
if key_text == path_el {
inner_val = val;
inner_val_found = true;
}
}
}
// if there is no value at that path
if inner_val_found == false {
return Err(MizeError::new()
// jesus christ, this is a pfusch...
.msg(format!(
"get_raw_from_cbor: Path '{}' not Found",
path.clone()
.into_iter()
.map(|v| v.to_owned())
.collect::<Vec<String>>()
.join("/")
)));
}
//path_iter.next();
let inner_path = path_iter.collect();
let ret_value = get_raw_from_cbor(inner_val, inner_path);
trace!("[ {} ] ret value: {:?}", "RET".yellow(), ret_value);
return ret_value;
}
_ => Err(MizeError::new().msg(format!(
"get_raw_from_cbor: value is not a map, text or bytes ... value: {:?}",
value
))),
}
}
trait IntoPath {
fn into_path(self) -> Vec<String>;
}
pub trait IntoItemData {
fn into_item_data(self) -> ItemData;
}
impl IntoPath for &str {
fn into_path(self) -> Vec<String> {
self.to_owned().split("/").map(|v| v.to_owned()).collect()
}
}
impl IntoPath for &[&str] {
fn into_path(self) -> Vec<String> {
self.into_iter().map(|v| (*v).to_owned()).collect()
}
}
impl IntoPath for Vec<String> {
fn into_path(self) -> Vec<String> {
self
}
}
impl IntoPath for Vec<&str> {
fn into_path(self) -> Vec<String> {
self.into_iter().map(|v| v.to_owned()).collect()
}
}
impl IntoItemData for &str {
fn into_item_data(self) -> ItemData {
ItemData::parse(self)
}
}
impl IntoItemData for String {
fn into_item_data(self) -> ItemData {
ItemData(self.into())
}
}
impl IntoItemData for &String {
fn into_item_data(self) -> ItemData {
ItemData(self.to_owned().into())
}
}
impl IntoItemData for CborValue {
fn into_item_data(self) -> ItemData {
ItemData(self)
}
}
impl IntoItemData for ItemData {
fn into_item_data(self) -> ItemData {
self
}
}
impl IntoItemData for i128 {
fn into_item_data(self) -> ItemData {
ItemData(self.into())
}
}
impl fmt::Display for ItemData {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "ItemData: ");
let display_writer = DisplayWriter(f);
self.0
.serialize(&mut serde_json::Serializer::pretty(display_writer))
.map_err(|serde_err| std::fmt::Error)
}
}
// thanks to: https://stackoverflow.com/a/61768916
struct DisplayWriter<'a, 'b>(&'a mut fmt::Formatter<'b>);
impl<'a, 'b> io::Write for DisplayWriter<'a, 'b> {
fn write(&mut self, bytes: &[u8]) -> std::result::Result<usize, std::io::Error> {
self.0
.write_str(&String::from_utf8_lossy(bytes))
.map_err(|err| std::io::Error::new(std::io::ErrorKind::Other, err))?;
Ok(bytes.len())
}
fn flush(&mut self) -> std::result::Result<(), std::io::Error> {
todo!()
}
}
impl PartialEq for ItemData {
fn eq(&self, other: &Self) -> bool {
let mut buf: Vec<u8> = Vec::new();
let mut buf_other: Vec<u8> = Vec::new();
ciborium::into_writer(&self.0, &mut buf);
ciborium::into_writer(&other.0, &mut buf_other);
buf == buf_other
}
}
impl Eq for ItemData {}

View File

@@ -1,3 +0,0 @@
#![ allow( warnings ) ]

View File

@@ -1,31 +0,0 @@
// console_log macro
// that can be copied into other files for debugging purposes
#[cfg(feature = "wasm-target")]
use wasm_bindgen::prelude::*;
#[cfg(feature = "wasm-target")]
#[wasm_bindgen]
extern "C" {
#[wasm_bindgen(js_namespace = console)]
fn log(s: &str);
}
#[cfg(feature = "wasm-target")]
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())})
}
#[macro_export]
macro_rules! add_parts {
($mize:expr, $($part:expr),+) => {
$(
let part = $part.create($mize.clone());
$mize.add_part(
part
);
)+
mize.init_parts();
};
}

View File

@@ -1,113 +0,0 @@
#![allow(warnings)]
static PROTO_VERSION: u8 = 1;
#[macro_export]
macro_rules! test_println {
($($arg:tt)*) => {
#[cfg(test)]
print!("[[ {} ]] ", <&str as colored::Colorize>::blue("TEST"));
#[cfg(test)]
print!($($arg)*);
#[cfg(test)]
print!("\n");
};
}
#[macro_export]
macro_rules! test_print {
($($arg:tt)*) => {
#[cfg(test)]
print!("[[ {} ]] ", <&str as colored::Colorize>::blue("TEST"));
#[cfg(test)]
print!($($arg)*);
};
}
// the core part has the code that can run on any platform
mod core {
pub mod config;
pub mod error;
pub mod id;
pub mod instance;
pub mod item;
pub mod macros;
pub mod memstore;
pub mod proto;
pub mod types;
}
pub use async_trait::async_trait;
pub use core::error::MizeError;
pub use core::error::MizeResult;
pub use core::instance::module::Module;
pub use core::instance::Mize;
pub use core::instance::{
DynMizePartGuard, MizePart, MizePartCreate, MizePartCreateGenerated, MizePartGenerated,
MizePartGuard,
};
pub use core::*;
pub use mize_macros::*;
use std::path::PathBuf;
// platform specific stuff
pub mod platform {
#[cfg(feature = "wasm-target")]
pub mod wasm;
#[cfg(feature = "target-os")]
pub mod os;
pub mod any {
//////////// instance_init
#[cfg(feature = "wasm-target")]
pub use super::wasm::wasm_instance_init as instance_init;
#[cfg(feature = "target-os")]
pub use super::os::os_instance_init as instance_init;
#[cfg(not(any(feature = "target-os", feature = "target-wasm ")))]
pub use super::super::instance_init;
//////////// load_module
#[cfg(feature = "target-os")]
pub use super::os::load_module;
#[cfg(feature = "wasm-target")]
pub use super::wasm::load_module;
#[cfg(not(any(feature = "target-os", feature = "target-wasm ")))]
pub use super::super::load_module;
//////////// fetch_module
#[cfg(feature = "target-os")]
pub use super::os::fetch_module;
#[cfg(feature = "wasm-target")]
pub use super::super::fetch_module;
#[cfg(not(any(feature = "target-os", feature = "target-wasm ")))]
pub use super::super::fetch_module;
}
}
pub fn instance_init(instance: &mut core::instance::Mize) -> MizeResult<()> {
Ok(())
}
pub fn load_module(
instance: &mut core::instance::Mize,
name: &str,
path: Option<String>,
) -> MizeResult<()> {
Ok(())
}
pub fn fetch_module(instance: &mut core::instance::Mize, name: &str) -> MizeResult<String> {
Ok("oh noooooooooooooo, something went really really wrong, if this ends up in the executable....".to_owned())
}

View File

@@ -1,166 +0,0 @@
use clap::ArgMatches;
use core::result::Result::Err;
use core::unimplemented;
use flume::bounded;
use home::home_dir;
use mize::mize_err;
use std::ffi::OsString;
use std::io::Read;
use std::path::PathBuf;
use std::str::FromStr;
use tracing::debug;
use mize::error::{IntoMizeResult, MizeError, MizeResult, MizeResultTrait};
use mize::instance::subscription::Subscription;
use mize::instance::subscription::Update;
use mize::instance::Mize;
use mize::item::{IntoItemData, ItemData};
use mize::platform::os::config_from_cli_args;
use mize::platform::os::fsstore::FileStore;
pub fn get(sub_matches: &ArgMatches) -> MizeResult<()> {
let instance = Mize::with_config(config_from_cli_args(sub_matches)?)?;
let id = sub_matches
.get_one::<String>("id")
.ok_or(MizeError::new().msg("No id Argument specified"))?;
let item = instance.get(id)?;
let recurse_count = sub_matches.get_count("recurse");
println!("{}", item.as_data_full()?);
return Ok(());
if recurse_count > 0 {
println!("{}", item.id());
println!("{}", item.as_data_full()?);
} else {
println!("{}", item);
};
return Ok(());
}
pub fn call(sub_matches: &ArgMatches) -> MizeResult<()> {
Ok(())
}
pub fn create(sub_matches: &ArgMatches) -> MizeResult<()> {
let mut instance = Mize::with_config(config_from_cli_args(sub_matches)?)?;
let item = instance.new_item()?;
println!("id: {}", item.id());
println!("with namespace: {}", item.id().namespace().as_real_string());
return Ok(());
}
pub fn is_running(sub_matches: &ArgMatches) -> MizeResult<()> {
println!("not implemented");
Err(mize_err!("not implemented"))
}
pub fn mount(sub_matches: &ArgMatches) -> MizeResult<()> {
Ok(())
}
pub fn run(sub_matches: &ArgMatches) -> MizeResult<()> {
let instance = Mize::with_config(config_from_cli_args(sub_matches)?)?;
instance.wait();
return Ok(());
}
pub fn set(sub_matches: &ArgMatches) -> MizeResult<()> {
let instance = Mize::with_config(config_from_cli_args(sub_matches)?)?;
let id = sub_matches
.get_one::<String>("id")
.ok_or(MizeError::new().msg("No id Argument specified"))?;
let value = sub_matches
.get_one::<String>("value")
.ok_or(MizeError::new().msg("No value Argument specified"))?;
instance.set_blocking(id, value.into_item_data())?;
Ok(())
}
pub fn show(sub_matches: &ArgMatches) -> MizeResult<()> {
let instance = Mize::with_config(config_from_cli_args(sub_matches)?)?;
let id = sub_matches
.get_one::<String>("id")
.ok_or(MizeError::new().msg("No id Argument specified"))?;
let item = instance.get(id)?;
let (tx, rx) = bounded::<Update>(4);
let sub = Subscription::from_sender(tx);
instance.sub(id, sub)?;
println!("item: {}", item.as_data_full()?);
for update in rx {
println!("####### GOT UPDATE #######");
println!("item: {}", update.new_item()?.as_data_full()?);
}
Ok(())
}
pub fn stop(sub_matches: &ArgMatches) -> MizeResult<()> {
Ok(())
}
pub fn gui(sub_matches: &ArgMatches) -> MizeResult<()> {
let mut instance = Mize::with_config(config_from_cli_args(sub_matches)?)?;
instance.load_module("mme")?;
instance.wait();
Ok(())
}
pub fn format_cbor(sub_matches: &ArgMatches) -> MizeResult<()> {
let mut input: Vec<u8> = Vec::new();
let stdin = std::io::stdin();
let mut handle = stdin.lock();
//handle.read_to_end(&mut input);
let cbor_data = ciborium::from_reader(handle)?;
let item_data = ItemData::from_cbor(cbor_data);
println!("{}", item_data);
Ok(())
}
pub fn run_from_module(cmd: &str, sub_matches: &ArgMatches) -> MizeResult<()> {
let default = OsString::default();
let mut sub_cmd_line: Vec<OsString> = sub_matches
.get_many::<OsString>("")
.unwrap()
.map(|e| e.to_owned())
.collect();
sub_cmd_line.insert(0, OsString::from_str(cmd)?);
let mut instance = Mize::with_config(config_from_cli_args(sub_matches)?)?;
debug!("loading module '{}'", cmd);
instance.load_module(cmd)?;
instance
.get_module(cmd)?
.run_cli(&instance, sub_cmd_line)
.ok_or(mize_err!(
"The Module '{cmd}' does not implement the run_cli() functionality"
))??;
Ok(())
}

View File

@@ -1,500 +0,0 @@
use std::collections::HashMap;
use std::path::PathBuf;
use clap::{ArgAction, ArgMatches};
use clap::{Arg, crate_version, Command};
use tokio::sync::mpsc;
use std::sync::Mutex;
use std::sync::Arc;
use colored::Colorize;
use std::env;
use tracing::{span_enabled, Instrument, Level};
use tracing::level_filters::LevelFilter;
use tracing_subscriber::filter::EnvFilter;
use tracing_subscriber::fmt::FormatEvent;
use tracing_subscriber::registry::LookupSpan;
use tracing_subscriber::fmt::FormatFields;
use tracing_subscriber::fmt::FmtContext;
use tracing_subscriber::fmt::format;
use tracing::Event;
use core::fmt;
use tracing_subscriber::fmt::FormattedFields;
use tracing::{trace, debug, info, warn, error};
use std::sync::atomic::AtomicUsize;
use tracing::{field::Visit, Id, Subscriber};
use tracing_core::Field;
use std::fmt::Write;
use crate::MizeError;
use crate::MizeResult;
use crate::id::MizeId;
use crate::item::ItemData;
static DEFAULT_LOG_LEVEL: LevelFilter = LevelFilter::WARN;
pub fn init_logger(cli_matches: &ArgMatches) {
// to save log messages, before the logger is setup
let mut log_messages: Vec<(Level, String)> = Vec::new();
log_messages.push((Level::INFO, "Starting Mize".to_owned()));
// builder with DEFAULT_LOG_LEVEL
//let mut builder = tracing_subscriber::fmt::Subscriber::builder();
// set loglevel
let mut log_level = DEFAULT_LOG_LEVEL;
log_messages.push((Level::TRACE, format!("set log level from DEFAULT_LOG_LEVEL to {}", DEFAULT_LOG_LEVEL)));
// check env RUST_LOG
let mut val_rust_log = level_from_env("RUST_LOG", log_level);
if let Some(level_rust_log) = val_rust_log.0 {
log_level = level_rust_log;
log_messages.push((Level::TRACE, format!("set log level from the Variable RUST_LOG to {}", level_rust_log)));
}
log_messages.append(&mut val_rust_log.1);
// check env MIZE_LOG
let mut val_mize_log = level_from_env("MIZE_LOG", log_level);
if let Some(level_mize_log) = val_mize_log.0 {
log_level = level_mize_log;
log_messages.push((Level::TRACE, format!("set log level from the Variable MIZE_LOG to {}", level_mize_log)));
}
log_messages.append(&mut val_mize_log.1);
// check -vvvvv flags
let v_flag_count = cli_matches.get_count("verbose");
let default_log_level_as_num = match DEFAULT_LOG_LEVEL {
LevelFilter::OFF => 0,
LevelFilter::ERROR => 1,
LevelFilter::WARN => 2,
LevelFilter::INFO => 3,
LevelFilter::DEBUG => 4,
LevelFilter::TRACE => 5,
};
let new_log_level_num = default_log_level_as_num + v_flag_count;
log_level = match new_log_level_num {
0 => LevelFilter::OFF,
1 => LevelFilter::ERROR,
2 => LevelFilter::WARN,
3 => LevelFilter::INFO,
4 => LevelFilter::DEBUG,
5 => LevelFilter::TRACE,
_ => {
log_messages.push((Level::TRACE, format!("the number of verbose (-v) flags passed ({}) exeeds the maximumg log_level", v_flag_count)));
LevelFilter::TRACE
},
};
log_messages.push((Level::TRACE, format!("set log level from the verbose flags to {}", log_level)));
// check --log-level arg
if let Some(log_level_arg_string) = cli_matches.get_one::<String>("log-level") {
let log_level_from_arg = match log_level_arg_string.to_owned().to_lowercase().as_str() {
"none" => LevelFilter::OFF,
"trace" => LevelFilter::TRACE,
"debug" => LevelFilter::DEBUG,
"info" => LevelFilter::INFO,
"warn" => LevelFilter::WARN,
"error" => LevelFilter::ERROR,
_ => {
log_messages.push((Level::WARN, format!(
"The argument to --log-level \"{}\" is not a valid log-level. loglevel stays at: {}"
, log_level_arg_string
, log_level
)));
log_level
}
};
log_level = log_level_from_arg;
log_messages.push((Level::TRACE, format!("set log level from the --log-level arg to {}", log_level_from_arg)));
}
// check --silent flag
if cli_matches.get_flag("silent") {
if log_level != DEFAULT_LOG_LEVEL {
log_messages.push((Level::WARN, format!(
"The loglevel was set by some method (it's not the DEFAULT_LOG_LEVEL) and is now overwritten by the --siltne to be OFF"
)))
}
log_level = LevelFilter::OFF;
}
// actually set the log_level on the builder
log_messages.push((Level::INFO, format!("loglevel is now {}", log_level)));
// filter loging
//let mut my_env_filter = tracing_subscriber::filter::EnvFilter::new("")
//.add_directive("[get_raw_from_cbor]=debug".parse().expect("error parsing log directive"))
//.add_directive("[MemStore::get_value_raw]=trace".parse().expect("error parsing log directive"))
//.add_directive("trace".parse().unwrap())
//.add_directive("[get_raw_from_cbor]=debug".parse().expect("error parsing log directive"))
//;
MinimalTracer::register();
//builder
//.with_env_filter(my_env_filter)
//.without_time()
//.event_format(MyFormatter)
//.with_max_level(log_level)
//.init();
// write out all log messages from log_messages
for (level, msg) in log_messages {
match level {
Level::ERROR => error!("{}", msg),
Level::WARN => warn!("{}", msg),
Level::INFO => info!("{}", msg),
Level::DEBUG => debug!("{}", msg),
Level::TRACE => trace!("{}", msg),
}
}
}
fn level_from_env(var_name: &str, log_level: LevelFilter) -> (Option<LevelFilter>, Vec<(Level, String)>) {
let mut log_messages: Vec<(Level, String)> = Vec::new();
let var_value = env::var(var_name);
match var_value {
Ok(val) => {
// set loglevel acording to RUST_LOG
let level = match val.to_lowercase().as_str() {
"none" => LevelFilter::OFF,
"trace" => LevelFilter::TRACE,
"debug" => LevelFilter::DEBUG,
"info" => LevelFilter::INFO,
"warn" => LevelFilter::WARN,
"error" => LevelFilter::ERROR,
_ => {
log_messages.push((Level::WARN, format!(
"The value of Environment Variable {}: \"{}\" is not a valid log-level. loglevel stays at: {}"
, var_name
, val
, log_level
)));
log_level
}
};
return (Some(level), log_messages)
},
Err(env::VarError::NotPresent) => {},
Err(env::VarError::NotUnicode(os_string)) => {
let os_string_as_hex : String = os_string.clone().into_encoded_bytes().iter()
.map(|b| format!("{:02x}", b).to_string())
.collect::<Vec<String>>()
.join("");
log_messages.push((Level::DEBUG, format!(
"value of Environment Variable {} was not a unicode valid string. Therefore ignoring it.
\n value with replacement char U+FFFD: {}
\n the WTF-8 raw bytes as hex: {}"
, var_name
, os_string.to_string_lossy(), os_string_as_hex
)));
},
};
return (None, log_messages);
}
struct MyFormatter;
impl<S, N> FormatEvent<S, N> for MyFormatter
where
S: tracing::Subscriber + for<'a> LookupSpan<'a>,
N: for<'a> FormatFields<'a> + 'static,
{
fn format_event(
&self,
ctx: &FmtContext<'_, S, N>,
mut writer: format::Writer<'_>,
event: &Event<'_>,
) -> fmt::Result {
// Format values from the event's's metadata:
let metadata = event.metadata();
// set format
/*
builder.format(|buf, record| {
match record.level() {
}
});
*/
let level_str = match metadata.level() {
&Level::TRACE => format!("[ {} ]", "TRACE".yellow()),
&Level::DEBUG => format!("[ {} ]", "DEBUG".green()),
&Level::INFO => format!("[ {} ]", "INFO".blue()),
&Level::WARN => format!("[ {} ]", "WARN".truecolor(245, 164, 66)),
&Level::ERROR => format!("[ {} ]", "ERROR".red()),
};
// with target
// write!(&mut writer, "{} {}: ", level_str, metadata.target())?;
write!(&mut writer, "{} ", level_str)?;
// Format all the spans in the event's span context.
if let Some(scope) = ctx.event_scope() {
for span in scope.from_root() {
write!(writer, "{}", span.name())?;
// `FormattedFields` is a formatted representation of the span's
// fields, which is stored in its extensions by the `fmt` layer's
// `new_span` method. The fields will have been formatted
// by the same field formatter that's provided to the event
// formatter in the `FmtContext`.
let ext = span.extensions();
let fields = &ext
.get::<FormattedFields<N>>()
.expect("will never be `None`");
// Skip formatting the fields if the span had no fields.
if !fields.is_empty() {
//write!(writer, "{{{}}}", fields)?;
}
write!(writer, ": ")?;
}
}
// Write fields on the event
ctx.field_format().format_fields(writer.by_ref(), event)?;
writeln!(writer)
}
}
// thanks: https://stackoverflow.com/a/75546824
pub struct StringVisitor<'a> {
string: &'a mut String,
}
impl<'a> StringVisitor<'a> {
pub(crate) fn new(string: &'a mut String) -> Self {
StringVisitor { string }
}
}
impl<'a> Visit for StringVisitor<'a> {
fn record_debug(&mut self, field: &Field, value: &dyn fmt::Debug) {
if field.name() == "message" {
write!(self.string, "{value:?} ").unwrap();
} else {
write!(self.string, "{} = {:?}; ", field.name(), value).unwrap();
}
}
}
struct LogFilter {
target: Option<String>,
level: Option<Level>,
}
pub struct MinimalTracer {
enabled: bool,
filters: Vec<LogFilter>,
level: Arc<Mutex<Level>>,
spans: Arc<Mutex<Vec<Id>>>,
span_levels: Arc<Mutex<HashMap<Id, Level>>>,
span_names: Arc<Mutex<HashMap<Id, String>>>,
span_names_to_enable: Arc<Mutex<Vec<String>>>,
level_no_span: Level,
}
fn string_to_level(string: &str) -> Option<Level> {
match string.to_lowercase().as_str() {
"info" => Some(Level::INFO),
"debug" => Some(Level::DEBUG),
"warn" | "warning" => Some(Level::WARN),
"trace" => Some(Level::TRACE),
"error" => Some(Level::ERROR),
_ => None,
}
}
impl MinimalTracer {
pub fn register() -> Result<(), tracing::subscriber::SetGlobalDefaultError> {
let mut enabled = true;
let mut span_names_to_enable = Vec::new();
let mut my_level = Level::ERROR;
let mut filters: Vec<LogFilter> = Vec::with_capacity(10);
if let Ok(env_value) = env::var("RUST_LOG") {
for filter in env_value.split(',') {
let mut target = Some(filter);
let mut level = None;
if let Some(equals_index) = target.unwrap().find('=') {
let (first, second) = filter.split_at(equals_index);
target = Some(first);
level = string_to_level(&second[1..])
} else {
my_level = string_to_level(target.unwrap()).unwrap();
}
let target_level = string_to_level(target.unwrap());
//if let Some(target_level) = target_level {
//level = Some(target_level);
//target = None;
//}
// in case target is a span name, add id to span_names_to_enable
// for now every target is also a span_name.....
// i would think everything, that contains a "." would be a span, and not a module
// targeet
if let Some(name) = target {
span_names_to_enable.push(name.to_owned());
}
filters.push(LogFilter {
target: target.map(|v| v.to_string()),
level,
});
}
} else {
enabled = false;
}
tracing::subscriber::set_global_default(MinimalTracer {
enabled, filters,
spans: Arc::new(Mutex::new(Vec::new())),
level: Arc::new(Mutex::new(my_level)),
span_names: Arc::new(Mutex::new(HashMap::new())),
span_names_to_enable: Arc::new(Mutex::new(span_names_to_enable)),
span_levels: Arc::new(Mutex::new(HashMap::new())),
level_no_span: my_level,
})
}
}
static AUTO_ID: AtomicUsize = AtomicUsize::new(1);
impl Subscriber for MinimalTracer {
fn enabled(&self, metadata: &tracing::Metadata<'_>) -> bool {
return true;
if metadata.is_span() {
return true;
let mut span_names_to_enable = self.span_names_to_enable.lock().unwrap();
if span_names_to_enable.contains(&metadata.name().to_owned()) {
return true;
}
}
if self.enabled {
if self.filters.is_empty() {
return true;
}
let mut matches: bool;
for filter in &self.filters {
matches = true;
if let Some(level) = filter.level {
if metadata.level() != &level {
matches = false;
}
}
if let Some(target) = &filter.target {
if !metadata.target().starts_with(target) {
matches = false;
}
}
if matches {
return true;
}
}
return false;
}
false
}
fn new_span(&self, _span: &tracing_core::span::Attributes<'_>) -> tracing_core::span::Id {
let mut span_names = self.span_names.lock().unwrap();
let mut span_levels = self.span_levels.lock().unwrap();
let mut span_names_to_enable = self.span_names_to_enable.lock().unwrap();
let id = Id::from_u64(AUTO_ID.fetch_add(1, std::sync::atomic::Ordering::Relaxed) as u64);
let span_name = _span.metadata().name().to_string();
span_names.insert(id.clone(), span_name.clone());
if span_names_to_enable.contains(&span_name) {
span_levels.insert(id.clone(), Level::TRACE);
} else {
span_levels.insert(id.clone(), Level::ERROR);
}
id
}
fn record(&self, _span: &tracing_core::span::Id, _values: &tracing_core::span::Record<'_>) {
}
fn record_follows_from(
&self,
_span: &tracing_core::span::Id,
_follows: &tracing_core::span::Id,
) {
}
fn event(&self, event: &tracing::Event<'_>) {
let metadata = event.metadata();
let level = metadata.level();
let my_level = self.level.lock().unwrap();
//println!("uuuuuuuuuuuu event name: {}", metadata.name());
//println!("uuuuuuuuuuuu my_level: {}", my_level);
//println!("uuuuuuuuuuuu event level: {}", level);
if *level > *my_level {
//println!("uuuuuuuuuuuu comparison");
//println!("BLOCKED EVENT")
return;
}
let target = metadata.target();
let mut text = String::new();
let mut visitor = StringVisitor::new(&mut text);
event.record(&mut visitor);
let level_str = match metadata.level() {
&Level::TRACE => format!("[ {} ]", "TRACE".yellow()),
&Level::DEBUG => format!("[ {} ]", "DEBUG".green()),
&Level::INFO => format!("[ {} ]", "INFO".blue()),
&Level::WARN => format!("[ {} ]", "WARN".truecolor(245, 164, 66)),
&Level::ERROR => format!("[ {} ]", "ERROR".red()),
};
//println!("{} {:?}", "META:".red(), metadata.in_current_span());
let mut spans = self.spans.lock().unwrap();
let mut span_names = self.span_names.lock().unwrap();
let span_text = spans.iter()
.map(|id| span_names.get(id).unwrap().to_owned())
.collect::<Vec<String>>()
.join("::");
println!("{level_str} {span_text} {text}");
}
fn enter(&self, _span: &tracing_core::span::Id) {
//println!("eeeeeeeeeeeeeeeee enter");
let mut spans = self.spans.lock().unwrap();
let mut level = self.level.lock().unwrap();
let mut span_levels = self.span_levels.lock().unwrap();
let mut span_names = self.span_names.lock().unwrap();
let name = span_names.get(_span).unwrap().to_owned();
*level = span_levels.get(_span).unwrap().to_owned();
spans.push(_span.clone());
//println!("my_level is now: {}", level);
}
fn exit(&self, _span: &tracing_core::span::Id) {
//println!("eeeeeeeeeeeeeeeee exit");
let mut level = self.level.lock().unwrap();
*level = self.level_no_span;
let mut spans = self.spans.lock().unwrap();
spans.pop();
}
}

View File

@@ -1,165 +0,0 @@
#![allow(warnings)]
use clap::builder::OsStr;
use clap::{crate_version, Arg, Command};
use clap::{ArgAction, ArgMatches};
use std::ffi::OsString;
use std::path::PathBuf;
use std::process::exit;
use colored::Colorize;
use mize::error::{MizeError, MizeResult};
use mize::item::ItemData;
use mize::platform::os::logging::init_logger;
use mize::{instance, Mize};
use std::env;
use std::io::Write;
use std::sync::Arc;
use tokio::sync::{mpsc, Mutex};
use tracing::level_filters::LevelFilter;
use tracing::Level;
use tracing_subscriber::filter::EnvFilter;
use tracing_subscriber::fmt::Subscriber;
use tracing::{debug, error, info, trace, warn};
mod cli;
static APPNAME: &str = "mize";
fn main() {
// welcome to the mize source code
// this is the main entry point for platforms with an os (eg: Linux, MacOS, Windows, BSD, ...)
// Also let's try to make the most best documented code ever. (eventually) so it's fun to work on
let cli_matches = cli_matches();
init_logger(&cli_matches);
// match command
let result = match cli_matches.subcommand() {
// mi daemon
Some(("run", sub_matches)) => cli::run(sub_matches),
Some(("is-running", sub_matches)) => cli::is_running(sub_matches),
// mi serve
Some(("serve", sub_matches)) => cli::stop(sub_matches),
// mi mount
Some(("mount", sub_matches)) => cli::mount(sub_matches),
// mi get
Some(("get", sub_matches)) => cli::get(sub_matches),
// mi set
Some(("set", sub_matches)) => cli::set(sub_matches),
// mi show
Some(("show", sub_matches)) => cli::show(sub_matches),
// mi call
Some(("call", sub_matches)) => cli::call(sub_matches),
// mi create
Some(("create", sub_matches)) => cli::create(sub_matches),
// mi gui
Some(("gui", sub_matches)) => cli::gui(sub_matches),
// mi format-cbor
Some(("format-cbor", sub_matches)) => cli::format_cbor(sub_matches),
// some unknown command passed
Some((cmd, sub_matches)) => cli::run_from_module(cmd, sub_matches),
None => Err(MizeError::new()
.msg("No subcommand was passed. use --help to list availavle comamnds.")),
};
if let Err(err) = result {
err.log();
exit(1);
}
}
fn cli_matches() -> clap::ArgMatches {
let main = Command::new(APPNAME)
.version(crate_version!())
.author("Sebastian Moser")
.about("The MiZe Command line tool")
.allow_external_subcommands(true)
.arg(
Arg::new("verbose")
.long("verbose")
.short('v')
.action(ArgAction::Count)
.global(true),
)
.arg(
Arg::new("log-level")
.long("log-level")
.value_name("LOGLEVEL")
.help("set the log-level to one of OFF, ERROR, WARN, INFO, DEBUG, TRACE")
.global(true),
)
.arg(
Arg::new("silent")
.long("silent")
.action(ArgAction::SetTrue)
.help("set the log-level to OFF")
.global(true),
)
.arg(
Arg::new("folder")
.short('f')
.long("folder")
.help("The folder the Instance stores all it's data and the socket for connections")
.global(true),
)
.arg(
Arg::new("config")
.short('c')
.long("config")
.help("overwrite config options")
.global(true),
)
.arg(
Arg::new("config-file")
.long("config-file")
.help("specify a config file")
.global(true),
)
.subcommand(
Command::new("run")
.aliases(["r"])
.about("Run a MiZe Instance"),
)
.subcommand(Command::new("stop").about("Stop a MiZe Instance"))
.subcommand(Command::new("mount").aliases(["m"]))
.subcommand(
Command::new("get")
.aliases(["g"])
.arg(Arg::new("id").help("The id or path to get"))
.arg(Arg::new("recurse").short('r').action(ArgAction::Count)),
)
.subcommand(
Command::new("set")
.aliases(["s"])
.arg(Arg::new("id").help("The id or path to set"))
.arg(Arg::new("value").help("The value to set the path to")),
)
.subcommand(
Command::new("show")
.aliases(["so"])
.arg(Arg::new("id").help("The id or path to sub to and show")),
)
.subcommand(Command::new("call").aliases(["c"]))
.subcommand(Command::new("create").aliases(["cr"]))
.subcommand(Command::new("is-running").aliases(["isr"]))
.subcommand(Command::new("gui"))
.subcommand(Command::new("format-cbor"))
.arg_required_else_help(true);
return main.get_matches();
}

View File

@@ -1,368 +0,0 @@
use ciborium::Value as CborValue;
use clap::ArgMatches;
use flate2::bufread::GzDecoder;
use serde::Deserialize;
use sha2::{Digest, Sha256, Sha512};
use std::env::VarError;
use std::fs;
use std::fs::File;
use std::io::copy;
use std::path::{Path, PathBuf};
use std::time::UNIX_EPOCH;
use tar::Archive;
use tracing::{debug, error, info, trace, warn};
use crate::error::{IntoMizeResult, MizeError, MizeResult};
use crate::id::MizeId;
use crate::instance::module::EmptyModule;
use crate::instance::store::Store;
use crate::instance::Mize;
use crate::item::{data_from_string, IntoItemData, ItemData};
use crate::memstore::MemStore;
use crate::{mize_err, Module};
use self::fsstore::FileStore;
pub mod fsstore;
pub mod logging;
#[cfg(target_family = "unix")]
mod unix_socket;
#[cfg(target_family = "unix")]
use crate::platform::os::unix_socket::UnixListener;
//mod web;
pub fn os_instance_init(instance: &mut Mize) -> MizeResult<()> {
// this is the code, that runs to initialize an Instance on a system with an os present.
////// load MIZE_CONFIG_FILE as the instance, which is item 0
match std::env::var("MIZE_CONFIG_FILE") {
Ok(config_file_path) => {
let config = config_from_file(config_file_path)?;
debug!("env var MIZE_CONFIG_FILE present");
instance.set_blocking("0", config)?;
trace!(
"config after MIZE_CONFIG_FILE env var: {}",
instance.get("self/config")?
);
}
Err(var_err) => match var_err {
VarError::NotPresent => {
debug!("env var MIZE_CONFIG_FILE NOT present");
}
VarError::NotUnicode(_) => {
warn!("env var MIZE_CONFIG_FILE is not Unicode, so not reading it")
}
},
};
////// load config from MIZE_CONFIG env var
match std::env::var("MIZE_CONFIG") {
Ok(config_string) => {
let config = data_from_string(config_string)?;
debug!("env var MIZE_CONFIG present");
instance.set_blocking("self/config", config)?;
trace!(
"config after MIZE_CONFIG env var: {}",
instance.get("self/config")?
);
}
Err(var_err) => match var_err {
VarError::NotPresent => {
debug!("env var MIZE_CONFIG NOT present")
}
VarError::NotUnicode(_) => {
warn!("env var MIZE_CONFIG is not Unicode, so not reading it")
}
},
};
////// if a config.store_path is set, upgrade to the filestore there
let mut test = instance.get("0")?.as_data_full()?;
let mut store_path = match instance.get("self/config/store_path")?.value_string() {
Ok(path) => path,
Err(e) => {
// the default store_path: $HOME/.mize
let home_dir = if let Some(dir) = std::env::home_dir() {
dir
} else {
return Err(mize_err!(
"could not get home_dir and store_path is not set in the config"
));
};
let store_path_buff = home_dir.join(".mize");
let store_path = store_path_buff.display().to_string();
instance.set_blocking(
"self/config/store_path",
store_path.clone().into_item_data(),
)?;
let store_path = instance.get("self/config/store_path")?.value_string()?;
println!("store_path: {}", store_path);
store_path
}
};
if FileStore::store_is_opened(store_path.to_owned(), instance)? {
// if the store is already opened, connect to the instance, that opened it and join
// it's namespace
#[cfg(target_family = "unix")]
{
info!("CONNECTING");
use crate::platform::os::unix_socket;
unix_socket::connect(instance, store_path.into())?;
return Ok(());
}
#[cfg(target_family = "windows")]
{
error!("CONNECTING... would connect, but that is not implemented on windows yet");
}
} else {
// else open it ourselves
let file_store = FileStore::new(store_path.as_str())?;
instance.migrate_to_store(Box::new(file_store))?;
let path = Path::new(&store_path).to_owned();
#[cfg(target_family = "unix")]
{
instance.add_listener(UnixListener::new(path)?)?;
}
#[cfg(target_family = "windows")]
{
warn!("would add a Listener on a local socket, but that is not yet implemented for windows");
}
}
Ok(())
}
pub fn seconds_since_modification(path: &Path) -> MizeResult<u64> {
let metadata = fs::metadata(path)?;
let modified = metadata.modified()?;
let duration = modified.duration_since(UNIX_EPOCH)?;
Ok(duration.as_secs())
}
pub fn get_module_hash(
instance: &mut Mize,
name: &str,
mut options: ItemData,
) -> MizeResult<(String, String)> {
let mut selector_data = instance.get("self/config/selector")?.as_data_full()?;
// handle the case, when we want a module for not the system we are running on...
println!("get_module_hash: name: {}", name);
let mut name_parts = name.split(".");
if name_parts
.next()
.ok_or(mize_err!("no 0th element in the modName"))?
== "cross"
{
let system = name_parts
.next()
.ok_or(mize_err!("no 1th element in the modName"))?;
let mod_name: String = name_parts.collect();
println!("get_module_hash: mod_name: {}", mod_name);
if mod_name == "" {
return Err(mize_err!("no 2nd element in the modName"));
}
selector_data.set_path("system", system)?;
selector_data.set_path("modName", mod_name)?;
} else {
selector_data.set_path("modName", name)?;
}
selector_data.merge(options);
// important to produce the same hash for the sama selector data
selector_data.sort_keys();
let selector_str = selector_data.to_json()?;
let mut hasher = Sha256::new();
hasher.update(selector_str.clone().as_bytes());
let result = hasher.finalize();
let mut hex: String = result
.iter()
.map(|b| format!("{:02x}", b).to_string())
.collect::<Vec<String>>()
.join("");
hex.truncate(32);
return Ok((hex, selector_str));
}
fn download_module(
instance: &mut Mize,
store_path: &str,
module_url: &str,
module_hash: &str,
module_name: &str,
module_selector: &str,
) -> MizeResult<()> {
let tmp_file_path_gz = format!("{}/{}.tar.gz", store_path, module_hash);
let tmp_gz_file = if !PathBuf::from(tmp_file_path_gz.clone()).exists() {
let mut tmp_gz_file = File::create(&tmp_file_path_gz)?;
debug!(
"downloading module '{}' with hash: {}",
module_name, module_hash
);
let response = http_req::request::get(
format!(
"http://{}/mize/dist/{}-{}.tar.gz",
module_url, module_hash, module_name
),
&mut tmp_gz_file,
)?;
let code = response.status_code();
if code != 200.into() {
return Err(mize_err!(
"failed to get module '{}' with hash '{}' and selector: {}",
module_name,
module_hash,
module_selector
)
.msg(format!(
"the http request to fetch the module returned a status code of: {}",
code
)));
}
tmp_gz_file
} else {
std::fs::File::open(tmp_file_path_gz.clone())?
};
let tar = GzDecoder::new(std::io::BufReader::new(tmp_gz_file));
let mut archive = Archive::new(tar);
//archive.set_mask(umask::Mode::parse("rwxrwxrwx")?.into());
let target_dir = format!("{}/modules/{}", store_path, module_hash);
std::fs::create_dir_all(target_dir.as_str())?;
archive.unpack(target_dir.as_str())?;
fs::remove_file(&tmp_file_path_gz)?;
Ok(())
}
pub fn fetch_module(instance: &mut Mize, module_name: &str) -> MizeResult<String> {
let module_name_with_slashes = module_name.replace(".", "/");
if let Ok(module_path) = instance
.get(format!(
"self/config/module_dir/{}",
module_name_with_slashes
))?
.value_string()
{
// in case a dir is configured, which holds the modules output
return Ok(module_path);
} else {
// if null, load it from the url
let module_url = instance.get("self/config/module_url")?.value_string()?;
let store_path = instance.get("self/config/store_path")?.value_string()?;
let (module_hash, module_selector) =
get_module_hash(instance, module_name, ItemData::new())?;
if !PathBuf::from(format!("{}/modules/{}", store_path, module_hash.as_str())).exists() {
// download module if it does not exist
download_module(
instance,
store_path.as_str(),
module_url.as_str(),
module_hash.as_str(),
module_name,
module_selector.as_str(),
)?;
}
return Ok(format!("{}/modules/{}", store_path, module_hash.as_str()));
}
}
pub fn load_module(instance: &mut Mize, module_name: &str, path: Option<String>) -> MizeResult<()> {
let module_path = if path.is_some() {
format!("{}/lib/libmize_module_{}.so", path.unwrap(), module_name)
} else {
format!(
"{}/lib/libmize_module_{}.so",
fetch_module(instance, module_name)?,
module_name
)
};
let lib = unsafe { libloading::Library::new(module_path)? };
let func: libloading::Symbol<
unsafe extern "C" fn(&mut Box<dyn Module + Send + Sync>, Mize) -> (),
> = unsafe { lib.get(format!("get_mize_module_{}", module_name).as_bytes())? };
let mut module: Box<dyn Module + Send + Sync> = Box::new(EmptyModule {});
unsafe { func(&mut module, instance.clone()) };
let mut modules_inner = instance.modules.lock()?;
module.init(&instance)?;
modules_inner.insert(module_name.to_owned(), module);
unsafe {
// dropping the lib, would (i suspect) free all memory, of the library's code, which would
// make the module's vtable point into empty memory
std::mem::forget(lib);
}
Ok(())
}
pub fn config_from_cli_args(matches: &ArgMatches) -> MizeResult<ItemData> {
let mut config = ItemData::new();
if let Some(config_file_path) = matches.get_one::<String>("config-file") {
config.merge(config_from_file(config_file_path.to_string())?);
trace!("config after --config-file arg: {}", config);
}
if let Some(config_string) = matches.get_one::<String>("config") {
config.merge(data_from_string(config_string.to_string())?);
trace!("config after --config arg: {}", config);
}
return Ok(config);
}
pub fn config_from_file(file_path: String) -> MizeResult<ItemData> {
let toml_string = fs::read_to_string(&file_path)
.mize_result_msg(format!("Could not read file {} as mize config", &file_path))?;
let item_data = ItemData::from_toml(toml_string.as_str())?;
return Ok(item_data);
}

Some files were not shown because too many files have changed in this diff Show More