Compare commits

...

38 Commits

Author SHA1 Message Date
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
206 changed files with 14593 additions and 33201 deletions

4
.gitignore vendored
View File

@@ -1,12 +1,16 @@
target
.direnv
# for nix
result
dist
rust_dist
deno_dist
node_modules
.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
gitignore

81
AGENTS.md Normal file
View File

@@ -0,0 +1,81 @@
# 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
## Codebase Structure
```
packages/
├── src/ # Core Mize framework (Rust)
├── marts/ # Mize parts collection (Rust/TypeScript)
└── macros/ # Mize macros
```
## Important
- usage of any git commands is strictly forbidden!
- 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)
## Key Components
All code written for, with or as part as mize (except for the small core) is written as a Mize Part. see `docs/part-system.md` for how that works.
To understand the main components of the architecture you can refer to `docs/architecture.md`.
Any proc macros should go into `./macros`.
the core mize library is in `./core`
any mize parts are in `./parts` or in their own repositories.
## 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
TODO: add better examples.
eg: adding a seccond line displaying a different value from the DB is not considered architectural. as it was achievable by only modifying the existing function and rsx! code.
### 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].

6021
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

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

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",
"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",
"dev": ""
},

View File

@@ -1,36 +1,45 @@
{
"version": "5",
"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:@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:esbuild@~0.27.3": "0.27.3",
"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: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-gfm@^4.0.1": "4.0.1",
"npm:remark-parse@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:unified@^11.0.5": "11.0.5",
"npm:unist-util-visit@^5.1.0": "5.1.0"
},
"npm": {
"@alloc/quick-lru@5.2.0": {
"integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw=="
"jsr": {
"@deno/esbuild-plugin@1.2.1": {
"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": {
"integrity": "sha512-MwBHVK60IiIHDcoMet78lxt6iw5gJOGSbNbOIVBHWVXIH4/Nq1+GQgLLGgI1KlnN86WDXsPudVaqYHKBIx7Eyw==",
"dependencies": [
@@ -185,196 +194,9 @@
"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": {
"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": {
"integrity": "sha512-VjFgDF/eB+Aklcy15TtOTLQeMjTo07k7KAjql8OK5Dirr7a6sJY4T1uVBDuTVG9VEmn1uUsohOpYnVfgC6/jyw==",
"dependencies": [
@@ -399,24 +221,6 @@
"@types/ms@2.1.0": {
"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": {
"integrity": "sha512-ypzHFE/wBzh+BlH6rrBgS5I/Z7RD21pGhZ2rltb/+ZrVM1awdZwjx7hE5XfuYgHWk9uvV5HLZN3SloevCAp3Bw==",
"dependencies": [
@@ -426,59 +230,21 @@
"@types/unist@3.0.3": {
"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": {
"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": {
"integrity": "sha512-bkXY9WsVpY7CvMhKSR6pZilZu9Ln5WDrKVBUXf2S443etkmEO4V58heTecXcUIsNsi4Rx8JUO4NfX1IcQl4deg=="
},
"ccount@2.0.1": {
"integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg=="
},
"chalk@5.6.2": {
"integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA=="
},
"character-entities@2.0.2": {
"integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ=="
},
"chownr@3.0.0": {
"integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g=="
},
"crelt@1.0.6": {
"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": {
"integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
"dependencies": [
@@ -494,22 +260,12 @@
"dequal@2.0.3": {
"integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA=="
},
"detect-libc@2.1.2": {
"integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="
},
"devlop@1.1.0": {
"integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==",
"dependencies": [
"dequal"
]
},
"enhanced-resolve@5.19.0": {
"integrity": "sha512-phv3E1Xl4tQOShqSte26C7Fl84EwUdZsyOuSSk9qtAGyyQs2s3jJzComh+Abf4g187lUUAvH+H26omrqia2aGg==",
"dependencies": [
"graceful-fs",
"tapable"
]
},
"esbuild@0.27.3": {
"integrity": "sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==",
"optionalDependencies": [
@@ -546,18 +302,6 @@
"escape-string-regexp@5.0.0": {
"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": {
"integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="
},
@@ -567,129 +311,18 @@
"format"
]
},
"fetch-blob@3.2.0": {
"integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==",
"dependencies": [
"node-domexception",
"web-streams-polyfill"
]
},
"format@0.2.2": {
"integrity": "sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww=="
},
"formdata-polyfill@4.0.10": {
"integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==",
"dependencies": [
"fetch-blob"
]
},
"github-slugger@2.0.0": {
"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": {
"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": {
"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": {
"integrity": "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw=="
},
@@ -1070,46 +703,12 @@
"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": {
"integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w=="
},
"ms@2.1.3": {
"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": {
"integrity": "sha512-DGAzpt6vo+sMDET/o8Zj26Bj2hbHrFsNUU8TtnzCyerO/OHksMFQnU9QEnPHVsOMstt/WnHXfC56j2r1syObWg==",
"dependencies": [
@@ -1119,40 +718,6 @@
"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": {
"integrity": "sha512-XTFYvNASMe5iPN0719nPrdItC9aU0ssC4v14mH1BCi1u0n1gAocqcujWUrByftZTbLhRtiKRyjYTSIOcr69UVQ==",
"dependencies": [
@@ -1190,52 +755,16 @@
"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": {
"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": {
"integrity": "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw=="
},
"tslib@2.8.1": {
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="
},
"typescript@5.9.3": {
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
"bin": true
},
"undici-types@6.21.0": {
"integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="
},
"unified@11.0.5": {
"integrity": "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==",
"dependencies": [
@@ -1275,10 +804,6 @@
"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": {
"integrity": "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==",
"dependencies": [
@@ -1296,45 +821,25 @@
"w3c-keyname@2.2.8": {
"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": {
"integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A=="
}
},
"workspace": {
"packageJson": {
"dependencies": [
"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:esbuild@~0.27.3",
"npm:mdast@3",
"npm:obsidian@^1.12.2",
"npm:react-dom@19.1.0",
"npm:react@19.1.0",
"npm:remark-frontmatter@5",
"npm:remark-gfm@^4.0.1",
"npm:remark-parse@11",
"npm:remark-stringify@11",
"npm:tailwindcss@4",
"npm:tslib@^2.6.2",
"npm:typescript@^5.9.3",
"npm:unified@^11.0.5",
"npm:unist-util-visit@^5.1.0"
]
}
"dependencies": [
"jsr:@deno/esbuild-plugin@^1.2.1",
"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"
]
}
}

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"

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

@@ -0,0 +1,166 @@
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 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, 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) -> MizeId {
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 })
}
}

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

@@ -0,0 +1,171 @@
#![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(&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)),
)
.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)
}

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

@@ -0,0 +1,3 @@
//pub extern "C" fn

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

@@ -0,0 +1,238 @@
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, 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").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 ()
}
}
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, 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").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 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, c_fun: CFun) -> ConfigOptId {
unsafe {
let c_str = CStr::from_ptr(name);
let string = c_str.to_string_lossy();
super::method(&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 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 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;
#[cfg(feature = "target-js")]
use wasm_bindgen::JsValue;
use core::result::Result;
use core::result::Result::Ok;
use std::collections::HashMap;
@@ -13,7 +15,7 @@ use tracing::{debug, error, info, trace, warn};
#[macro_export]
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>;
@@ -24,8 +26,8 @@ pub trait MizeResultTrait<T> {
}
pub trait IntoMizeResult<T, S> {
fn mize_result(self) -> MizeResult<T>;
fn mize_result_msg(self, msg: S) -> MizeResult<T>
fn mres(self) -> MizeResult<T>;
fn mres_msg(self, msg: S) -> MizeResult<T>
where
S: std::fmt::Display;
}
@@ -125,16 +127,16 @@ impl MizeError {
}
pub fn log(self) -> MizeError {
error!("MizeError envountered!");
println!("[ {} ] MizeError encountered!", "ERROR".red());
if let Some(ref location) = self.code_location {
error!("[ {} ] {}", "LOCATION".yellow(), location);
println!("[ {} ] {}", "LOCATION".yellow(), location);
};
error!("[ {} ] {}", "BACKTRACE".yellow(), self.backtrace);
println!("[ {} ] {}", "BACKTRACE".yellow(), self.backtrace);
for msg in &self.messages {
error!("[ {} ] {}", "MSG".yellow(), msg);
println!("[ {} ] {}", "MSG".yellow(), msg);
}
self
@@ -261,7 +263,7 @@ where
E: std::fmt::Display,
{
#[track_caller]
fn mize_result(self) -> MizeResult<T> {
fn mres(self) -> MizeResult<T> {
let caller_location = std::panic::Location::caller();
match self {
@@ -274,7 +276,7 @@ where
}
}
#[track_caller]
fn mize_result_msg(self, msg: S) -> MizeResult<T>
fn mres_msg(self, msg: S) -> MizeResult<T>
where
S: std::fmt::Display,
{
@@ -346,3 +348,20 @@ impl From<MizeError> for MizeErrorWhichImplementsError {
}
impl std::error::Error for MizeErrorWhichImplementsError {}
// i hate rust sometimes.
// why can't i impl IntoMizeResult for any type. and the extra concrete impl of it for JsValue overrides the more generic one.
#[cfg(feature = "target-js")]
pub trait IntoMizeResultJsValue<T> {
fn mres(self) -> MizeResult<T>;
}
#[cfg(feature = "target-js")]
impl<T> IntoMizeResultJsValue<T> for Result<T, JsValue> {
fn mres(self) -> MizeResult<T> {
match self {
Ok(value) => Ok(value),
Err(err) => Err(mize_err!("From JsValue: {:?}", err)),
}
}
}

View File

@@ -1,17 +1,11 @@
use core::fmt;
use interner::{
shared::{SharedPool, SharedString, SharedVecString},
Pooled,
shared::{SharedString, SharedVecString},
};
use std::collections::hash_map::RandomState;
use std::path::Path;
use std::{collections::binary_heap::Iter, fmt::write, hash::Hash};
use std::hash::Hash;
use crate::error::{MizeError, MizeResult, MizeResultTrait};
use crate::{
instance::{store::Store, Mize},
mize_err,
};
use crate::{get_global_mize, error::MizeResult};
use crate::{instance::Mize, mize_err};
#[derive(Debug, Clone, Hash, Eq, PartialEq)]
pub struct MizeId {
@@ -38,6 +32,24 @@ pub trait IntoMizeId {
}
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 {
self.path
.iter()
@@ -59,7 +71,7 @@ impl MizeId {
pub fn after_store_part(&self) -> Vec<String> {
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();
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 {
fn to_mize_id(self, instance: &Mize) -> MizeResult<MizeId> {
fn to_mize_id(self, _instance: &Mize) -> MizeResult<MizeId> {
Ok(self)
}
}

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: u64 = conn_id_str.parse()?;
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 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)?;

View File

@@ -1,32 +1,29 @@
use async_trait::async_trait;
use colored::Colorize;
use core::fmt;
use flume::{bounded, unbounded, Receiver, Sender};
use std::time::Duration;
use flume::{bounded, unbounded, 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::sync::{Arc, Mutex, RwLock};
use std::{thread, vec};
use tracing::{debug, error, info, trace, warn, Instrument};
use uuid::Uuid;
use tracing::{debug, info, trace};
use crate::config::ConfigOptNameAndMize;
use crate::config::{gather_config, ConfigOpt};
use crate::error::{IntoMizeResult, MizeError, MizeResult, MizeResultTrait};
use crate::set_global_mize;
use crate::config::{ConfigOpt, ConfigOptId, MizeFun};
use crate::error::{MizeError, MizeResult};
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::instance::updater::updater_thread;
use crate::item::{Item, ItemData};
use crate::memstore::MemStore;
use crate::mize_err;
use crate::proto::MizeMessage;
use crate::{mize_err, Module};
use crate::error::IntoMizeResult;
use self::connection::{ConnListener, Connection};
use self::updater::handle_operation;
@@ -40,7 +37,6 @@ use core::future::Future;
use std::thread::JoinHandle;
pub mod connection;
pub mod module;
pub mod msg_thread;
pub mod store;
pub mod subscription;
@@ -61,15 +57,18 @@ pub struct Mize {
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>>>>>,
pub(crate) listeners: Arc<
Mutex<HashMap<String, Arc<dyn Fn(MizeMessage, &mut Mize) -> MizeResult<()> + Send + Sync>>>,
>,
part_names: Arc<Mutex<Vec<&'static str>>>,
pub parts: Arc<Mutex<HashMap<MizeId, Option<Box<dyn MizePart + Sync + Send>>>>>,
//pub(crate) platform: Arc<Mutex<Box<dyn MizePlatform>>>, // should we do that instead of all the #cfg(faturs=targt-smth) in lib.rs
part_names: Arc<Mutex<Vec<&'static str>>>, // exists, so that we know the ordering in which parts were loaded
pub(crate) config_opts: Arc<Mutex<HashMap<String, ConfigOpt>>>,
pub(crate) config_opts: Arc<Mutex<HashMap<MizeId, ConfigOpt>>>,
// the namespace the instance operates in
pub(crate) namespace: Arc<Mutex<Namespace>>,
@@ -78,134 +77,90 @@ pub struct Mize {
// 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)>>>,
threads: Arc<Mutex<Vec<(u32, String, Option<JoinHandle<MizeResult<()>>>)>>>,
next_thread_id: Arc<Mutex<u32>>,
give_msg_wait: Arc<Mutex<HashMap<MizeId, Vec<Sender<ItemData>>>>>,
create_msg_wait: Arc<Mutex<Option<Sender<MizeId>>>>,
pub cur_eval_part: Arc<Mutex<Option<MizeId>>>,
pub native_values: Arc<Mutex<HashMap<u32, Box<dyn Any + Send + Sync>>>>,
pub next_native_value_id: Arc<Mutex<u32>>,
pub fun_values: Arc<Mutex<HashMap<MizeId, MizeFun>>>,
pub next_fun_values_id: Arc<Mutex<u32>>,
// generate mode: when true, part_smth functions record metadata instead of executing
pub generate_mode: Arc<RwLock<bool>>,
// recorded metadata per part (keyed by part id) while in generate mode
pub part_metadata: Arc<Mutex<HashMap<MizeId, PartMetadata>>>,
#[cfg(feature = "async")]
pub(crate) runtime: Arc<Mutex<Runtime>>,
pub runtime: Arc<Mutex<Runtime>>,
}
// the public Mize API
/// Metadata recorded about a part while in generate mode
#[derive(Clone, Debug, Default)]
pub struct PartMetadata {
pub part_name: String,
pub opts: Vec<OptMetadata>,
pub methods: Vec<MethodMetadata>,
pub effects: Vec<MethodMetadata>,
pub threads: Vec<MethodMetadata>,
pub natives: Vec<NativeMetadata>,
}
#[derive(Clone, Debug)]
pub struct OptMetadata {
pub name: String,
pub id: MizeId,
pub default_val: Option<ItemData>,
}
#[derive(Clone, Debug)]
pub struct MethodMetadata {
pub name: String,
pub id: MizeId,
pub kind: String, // "method", "effect", "thread"
}
#[derive(Clone, Debug)]
pub struct NativeMetadata {
pub name: String,
pub type_name: String,
pub associated: Vec<String>,
pub methods: Vec<String>,
}
pub struct InstanceRef {
inner: Arc<Mutex<Mize>>,
}
pub struct PartRef {
id: MizeId,
}
impl PartRef {
pub fn into_any(self) -> Box<dyn Any> {
Box::new(self)
}
}
impl MizePart for PartRef {
fn id(&self) -> MizeId {
self.id.clone()
}
}
pub trait PartToNative {
fn from_mize_id(id: MizeId) -> Self;
}
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(())
}
pub trait MizePart {
fn id(&self) -> MizeId;
}
impl Mize {
@@ -229,6 +184,7 @@ impl Mize {
parts: Arc::new(Mutex::new(HashMap::new())),
part_names: Arc::new(Mutex::new(Vec::new())),
config_opts: Arc::new(Mutex::new(HashMap::new())),
listeners: Arc::new(Mutex::new(HashMap::new())),
connections,
subs,
id_pool,
@@ -236,16 +192,22 @@ impl Mize {
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,
cur_eval_part: Arc::new(Mutex::new(None)),
native_values: Arc::new(Mutex::new(HashMap::new())),
next_native_value_id: Arc::new(Mutex::new(0)),
fun_values: Arc::new(Mutex::new(HashMap::new())),
next_fun_values_id: Arc::new(Mutex::new(0)),
generate_mode: Arc::new(RwLock::new(false)),
part_metadata: Arc::new(Mutex::new(HashMap::new())),
#[cfg(feature = "async")]
runtime: Arc::new(Mutex::new(
Runtime::new().mize_result_msg("Could not create async runtime")?,
Runtime::new().mres_msg("Could not create async runtime")?,
)),
};
@@ -254,11 +216,11 @@ impl Mize {
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)?;
instance.spawn_background("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)?;
instance.spawn_background("updater_thread", closure_two)?;
}
// set up async update "threads" when using wasm
@@ -296,7 +258,7 @@ impl Mize {
let mut instance = Mize::empty()?;
instance.init();
instance.init()?;
debug!(
"instance inited with config: {}",
@@ -306,57 +268,60 @@ impl Mize {
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 fn id_new_dots(&mut self, name: &str) -> MizeResult<MizeId> {
let parts = name.split(".").map(|v| v.to_owned()).collect::<Vec<String>>();
self.new_id(parts)
}
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 get_part(&mut self, name: &str) -> MizeResult<PartRef> {
let id = self.id_new_dots(name)?;
Ok(PartRef { id })
}
pub fn has_part(&mut self, name: &str) -> MizeResult<bool> {
let id = self.id_new_dots(name)?;
Ok(self.config_opts.lock().unwrap().contains_key(&id))
}
pub fn add_listener(
&mut self,
name: &str,
handler: Box<dyn Fn(MizeMessage, &mut Mize) -> MizeResult<()> + Send + Sync>,
) -> MizeResult<()> {
let name = name.to_owned();
let mut listeners = self.listeners.lock().unwrap();
listeners.insert(name, Arc::new(handler));
Ok(())
}
pub fn init(&mut self) -> MizeResult<()> {
// gather options
gather_config(self)?;
// set global mize
set_global_mize(self.clone())?;
// platform specific init code
crate::platform::any::instance_init(self)?;
crate::platform::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)?;
// load the parts, as specified in the parts config
match self
.get_config("parts")
{
Ok(parts_to_load) => {
for part_name in parts_to_load.cbor().as_array().ok_or("config parts was not an array")? {
let part_name = part_name.as_text().ok_or("part name was not a string")?;
if part_name.trim().is_empty() || self.has_part(part_name)? {
continue;
}
self.load_part(part_name)?;
}
}
Err(err) => {
// no load_modules option is set
Err(_err) => {
// no parts to load
}
}
debug!("INSTANCE INIT DONE");
debug!("MIZE INIT DONE");
Ok(())
}
@@ -364,13 +329,34 @@ impl Mize {
trace!("[ {} ] Instance::with_config()", "CALL".yellow());
trace!("config: {}", config);
let mut instance = Mize::empty()?;
let mut mize = Mize::empty()?;
instance.set_blocking("0", config);
let mut opts = mize.config_opts.lock().unwrap();
let mut config = config.clone();
for mut path in config.get_paths_recursive()? {
if path.starts_with("/") {
path.remove(0);
}
let path_split = path
.split("/")
.map(|s| s.to_owned())
.collect::<Vec<String>>();
let path_string = path_split.join(".");
if let Ok(val) = config.get_path(path_split) {
let opt = ConfigOpt {
name: path.clone(),
val: Some(val),
fun: None,
deps: Vec::new(),
};
opts.insert(mize.clone().id_new_dots(&path_string)?, opt);
}
}
drop(opts);
instance.init()?;
mize.init()?;
Ok(instance)
Ok(mize)
}
pub fn migrate_to_store(&self, new_store: Box<dyn Store>) -> MizeResult<()> {
@@ -396,7 +382,7 @@ impl Mize {
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 connection = self.get_connection_by_ns(self.get_namespace()?)?;
let msg = MizeMessage::new_create(connection.id);
@@ -409,7 +395,6 @@ impl Mize {
drop(msg_wait_inner);
let id = rx.recv()?;
println!("new_item namespace: {:?}", id.namespace());
return Ok(Item::new(id, self));
}
@@ -512,29 +497,111 @@ impl Mize {
Ok(id)
}
pub fn load_module(&mut self, name: &str) -> MizeResult<()> {
crate::platform::any::load_module(self, name, None)
pub fn load_part(&mut self, name: &str) -> MizeResult<()> {
tracing::debug!("loading part: {name}");
crate::platform::load_part(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 fetch_part(&mut self, name: &str) -> MizeResult<String> {
// platform specific way to fetch a part
crate::platform::fetch_part(self, name)
}
pub fn load_module_at(&mut self, name: &str, path: String) -> MizeResult<()> {
pub fn load_part_at(&mut self, name: &str, path: String) -> MizeResult<()> {
// platform specific init code
crate::platform::any::load_module(self, name, Some(path))
crate::platform::load_part(self, name, Some(path))
}
pub fn get_module(&mut self, name: &str) -> MizeResult<Box<dyn Module + Send + Sync>> {
let inner = self.modules.lock()?;
// ===== generate mode accessors =====
let module = inner
.get(name)
.ok_or(mize_err!("Couldn't get_module('{name}')"))?
.clone_module();
/// Enable or disable generate mode. When enabled, part_smth functions record
/// metadata (methods, opts, natives) instead of performing their normal operations.
pub fn set_generate_mode(&self, enabled: bool) -> MizeResult<()> {
let mut mode = self.generate_mode.write().map_err(|_| mize_err!("generate_mode lock poisoned"))?;
*mode = enabled;
Ok(())
}
Ok(module)
/// Check if generate mode is currently enabled.
pub fn is_generate_mode(&self) -> MizeResult<bool> {
let mode = self.generate_mode.read().map_err(|_| mize_err!("generate_mode lock poisoned"))?;
Ok(*mode)
}
/// Record an opt declaration while in generate mode.
pub fn record_opt(&self, name: &str, id: MizeId, default_val: Option<ItemData>) -> MizeResult<()> {
let cur_part = self.cur_eval_part.lock()?.clone()
.ok_or_else(|| mize_err!("no cur_eval_part set"))?;
let mut metadata_map = self.part_metadata.lock()?;
let metadata = metadata_map.entry(cur_part.clone()).or_insert_with(|| PartMetadata {
part_name: cur_part.dot(),
..Default::default()
});
metadata.opts.push(OptMetadata {
name: name.to_string(),
id,
default_val,
});
Ok(())
}
/// Record a method/effect/thread declaration while in generate mode.
pub fn record_method(&self, name: &str, id: MizeId, kind: &str) -> MizeResult<()> {
let cur_part = self.cur_eval_part.lock()?.clone()
.ok_or_else(|| mize_err!("no cur_eval_part set"))?;
let mut metadata_map = self.part_metadata.lock()?;
let metadata = metadata_map.entry(cur_part.clone()).or_insert_with(|| PartMetadata {
part_name: cur_part.dot(),
..Default::default()
});
let m = MethodMetadata {
name: name.to_string(),
id,
kind: kind.to_string(),
};
match kind {
"effect" => metadata.effects.push(m),
"thread" => metadata.threads.push(m),
_ => metadata.methods.push(m),
}
Ok(())
}
/// Record a native type inclusion while in generate mode.
pub fn record_native(&self, name: &str, type_name: &str, associated: Vec<String>, methods: Vec<String>) -> MizeResult<()> {
let cur_part = self.cur_eval_part.lock()?.clone()
.ok_or_else(|| mize_err!("no cur_eval_part set"))?;
let mut metadata_map = self.part_metadata.lock()?;
let metadata = metadata_map.entry(cur_part.clone()).or_insert_with(|| PartMetadata {
part_name: cur_part.dot(),
..Default::default()
});
metadata.natives.push(NativeMetadata {
name: name.to_string(),
type_name: type_name.to_string(),
associated,
methods,
});
Ok(())
}
/// Get recorded metadata for a specific part.
pub fn get_metadata(&self, part_id: &MizeId) -> MizeResult<Option<PartMetadata>> {
let metadata_map = self.part_metadata.lock()?;
Ok(metadata_map.get(part_id).cloned())
}
/// Get all recorded metadata (for all parts).
pub fn get_all_metadata(&self) -> MizeResult<Vec<PartMetadata>> {
let metadata_map = self.part_metadata.lock()?;
Ok(metadata_map.values().cloned().collect())
}
/// Clear all recorded metadata.
pub fn clear_metadata(&self) -> MizeResult<()> {
let mut metadata_map = self.part_metadata.lock()?;
metadata_map.clear();
Ok(())
}
pub fn namespace_from_string(&self, ns_str: String) -> MizeResult<Namespace> {
@@ -551,12 +618,12 @@ impl Mize {
}
pub fn get_namespace(&self) -> MizeResult<Namespace> {
let mut namespace_inner = self.namespace.lock()?;
let 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()?;
let self_namespace_inner = self.self_namespace.lock()?;
return Ok(self_namespace_inner.clone());
}
@@ -564,9 +631,9 @@ impl Mize {
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));
pub fn add_conn_listener<T: ConnListener + 'static>(&mut self, listener: T) -> MizeResult<()> {
let instance_clone = self.clone();
self.spawn_background("some_listener", move || listener.listen(instance_clone));
Ok(())
}
@@ -590,7 +657,7 @@ impl Mize {
let ns_of_peer_str = self
.get(format!(
"inst/con_by_id/{}/peer/0/config/namespace",
"inst/con_by_id/{}/peer/self/config/namespace",
conn_id
))?
.value_string()?;
@@ -614,6 +681,21 @@ impl Mize {
}
pub fn report_err(&self, err: MizeError) {
#[cfg(feature = "target-js")] unsafe {
use wasm_bindgen::prelude::wasm_bindgen;
#[wasm_bindgen]
extern "C" {
#[wasm_bindgen(js_namespace = console)]
fn log(s: &str);
}
macro_rules! console_log {
// Note that this is using the `log` function imported above during
// `bare_bones`
($($t:tt)*) => ({ log(&format_args!($($t)*).to_string())})
}
console_log!("reported_err: {:?}", err);
}
err.log();
}
@@ -631,11 +713,10 @@ impl Mize {
"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()?;
let conn_inner = self.connections.lock()?;
for connection in conn_inner.iter() {
if connection.id == conn_id {
@@ -650,7 +731,7 @@ impl Mize {
}
pub fn get_connection_by_ns(&self, ns: Namespace) -> MizeResult<Connection> {
let mut conn_inner = self.connections.lock()?;
let conn_inner = self.connections.lock()?;
for connection in conn_inner.iter() {
if connection.ns == Some(ns.clone()) {
@@ -664,26 +745,28 @@ impl Mize {
));
}
pub fn spawn(
pub fn spawn_and_wait(
&mut self,
name: &str,
func: impl FnOnce() -> MizeResult<()> + Send + 'static,
) -> MizeResult<()> {
let mize_clone = self.clone();
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 _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;
let _my_thread_id = my_thread_id_no_mutex_guard;
func()?;
if let Err(err) = func() {
mize_clone.report_err(err);
}
let mut threads_inner = thread_mutex.lock()?;
//let mut threads_inner = thread_mutex.lock()?;
/*
*threads_inner = threads_inner
.clone()
.into_iter()
@@ -692,6 +775,7 @@ impl Mize {
(_, _) => true,
})
.collect();
*/
debug!("thread '{}' stopped", name_to_move);
Ok(())
};
@@ -699,7 +783,10 @@ impl Mize {
*next_thread_id += 1;
#[cfg(feature = "target-os")]
thread::spawn(move || to_spawn());
let handle = thread::spawn(move || to_spawn());
#[cfg(feature = "target-os")]
threads_inner.push((*next_thread_id, name.to_owned(), Some(handle)));
#[cfg(feature = "target-wasm ")]
{
@@ -711,6 +798,57 @@ impl Mize {
Ok(())
}
pub fn spawn_background(
&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()?;
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 || -> () {
debug!("spawning thread: {}", name_to_move);
let _my_thread_id = my_thread_id_no_mutex_guard;
let res = func();
if let Err(e) = res {
e.log();
}
//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);
};
*next_thread_id += 1;
#[cfg(feature = "target-os")]
thread::spawn(move || to_spawn());
threads_inner.push((*next_thread_id, name.to_owned(), None));
#[cfg(feature = "target-js")]
{
//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()?;
@@ -744,7 +882,7 @@ impl Mize {
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()));
threads_inner.push((*next_thread_id, name.to_owned(), None));
let runtime_inner = self.runtime.lock().unwrap();
runtime_inner.spawn(func);
@@ -777,7 +915,7 @@ impl Mize {
.lock()
.expect("mutex lock failed in spawn_async_blocking");
threads_inner.push((*next_thread_id, name.to_owned()));
threads_inner.push((*next_thread_id, name.to_owned(), None));
let runtime_inner = self.runtime.lock().unwrap();
let handle = runtime_inner.handle().to_owned();
*next_thread_id += 1;
@@ -786,6 +924,7 @@ impl Mize {
drop(threads_inner);
drop(next_thread_id);
handle.enter();
let result = handle.block_on(func);
return result;
@@ -794,7 +933,7 @@ impl Mize {
pub fn wait(&self) {
info!("Instance main thread waiting");
loop {
thread::sleep_ms(10000000)
thread::sleep(Duration::from_secs(100000))
}
}
@@ -805,93 +944,78 @@ impl Mize {
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)
let mut id;
let mut split = name.split(".");
if split.nth(0).unwrap() == "self" {
let cur_part = self.cur_eval_part.lock()?.clone().ok_or(mize_err!("get_config called but no curr eval part set"))?;
id = cur_part.clone();
for el in split {
id = id.join(el)?;
}
} else {
id = self.clone().id_new_dots(name)?;
}
let mut opt = config_opts
.get_mut(&id)
.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(),
// in case it is not cached run the fun if it exists
if let Some(fun) = &opt.fun {
let res = fun()?;
opt.val = Some(res.clone());
return Ok(res);
}
Err(mize_err!("ConfigOpt {name} has no fun and no cached value"))
}
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 dyn_call(&mut self, part_id: MizeId, method: &str, args: ItemData) -> MizeResult<ItemData> {
debug!("dyn call {}.{} with args: {}", part_id, method, args);
let mut fun_values = self.fun_values.lock()?;
let id = part_id.clone().join(method)?;
//let part = opts.get_mut(&id).ok_or(mize_err!("part {} is not loaded", part_id.dot()))?;
let fun = fun_values.remove(&id).ok_or(mize_err!("fun value for {id} not found"))?;
drop(fun_values);
let calling_part = self.clone().cur_eval_part()?;
self.clone().set_cur_eval_part(Some(part_id));
let res = fun(args);
self.clone().set_cur_eval_part(calling_part);
let mut fun_values = self.fun_values.lock()?;
fun_values.insert(id, fun);
return res;
}
pub fn get_part_native<T: MizePart + Send + Sync + 'static>(
pub fn cur_eval_part(&mut self) -> MizeResult<Option<MizeId>> {
Ok(self.cur_eval_part.lock()?.clone())
}
pub fn set_cur_eval_part(&mut self, part: Option<MizeId>) -> MizeResult<()> {
let mut guard = self.cur_eval_part.lock()?;
*guard = part;
Ok(())
}
pub fn get_part_gen<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),
})
) -> MizeResult<T> {
let dyn_part = self.get_part(name)?;
let concrete_part = dyn_part
.into_any()
.downcast::<T>()
.map_err(|_| mize_err!("failed to downcast"))?;
Ok(*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
pub fn id_path(&self, id: &MizeId) -> MizeResult<String> {
Ok(id.path().join("/"))
}
}

View File

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

View File

@@ -1,5 +1,3 @@
use tracing_subscriber::registry::Data;
use crate::item::IntoItemData;
use super::*;
@@ -43,7 +41,7 @@ fn test_sort_keys() -> MizeResult<()> {
fn test_get_module_hash() -> MizeResult<()> {
let mut instance = Mize::empty()?;
use crate::platform::os::get_module_hash;
use crate::platform::os::get_part_hash;
let selector_toml = r#"
mize_version = "0.0.0"
system = "x86_64-unknown-linux-gnu"
@@ -52,10 +50,10 @@ fn test_get_module_hash() -> MizeResult<()> {
instance.set_blocking("0/config/selector", ItemData::from_toml(&selector_toml)?)?;
let (hash, selector) = get_module_hash(&mut instance, "String", ItemData::new())?;
let (hash, selector) = get_part_hash(&mut instance, "String", ItemData::new())?;
test_println!("hash: {}", hash);
test_println!("selector: {}", selector);
println!("hash: {}", hash);
println!("selector: {}", selector);
assert_eq!(hash, "e96edf60216688314efc263c1aa8d6ce".to_owned());
@@ -70,7 +68,7 @@ fn test_set_store_path() -> MizeResult<()> {
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())?;
@@ -252,12 +250,3 @@ fn test_item_merge() -> MizeResult<()> {
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

@@ -215,6 +215,19 @@ fn handle_msg(msg: &mut MizeMessage, instance: &Mize) -> MizeResult<()> {
}
return Ok(());
}
MessageCmd::Custom(type_val) => {
let listeners = instance.listeners.lock()?;
let type_str = type_val.value_string()?;
let msg = msg.clone();
let mut mize = instance.clone();
println!("type_str: {}", type_str);
println!("listeners: {:?}", listeners.keys());
if let Some(listener) = listeners.get(&type_str) {
let listener = listener.clone();
drop(listeners);
listener(msg, &mut mize)?;
}
}
_ => {
return Err(mize_err!("got a message, that is not handeled"));
}

1668
core/src/item.rs Normal file

File diff suppressed because it is too large Load Diff

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

@@ -0,0 +1,280 @@
#![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!($($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<()> {
println!("{}", 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 flume;
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,
};
pub use mize_macros::*;
use std::path::PathBuf;
use std::sync::{Arc, Mutex, OnceLock};
/// 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.
#[unsafe(no_mangle)]
pub extern "C" fn mize_rust_hack_set_global(mize: Arc<Mutex<Mize>>) {
let _ = GLOBAL_MIZE.set(mize);
}
/// 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(mize: &mut Mize, part_name: &str, path: Option<String>) -> MizeResult<()> {
#[cfg(feature = "target-os")] {
os::load_part(mize, part_name, path)?;
}
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

@@ -145,7 +145,7 @@ fn id_to_u64(id: MizeId) -> MizeResult<u64> {
}
fn str_to_u64(id_str: &str) -> MizeResult<u64> {
id_str.parse().mize_result_msg(format!(
id_str.parse().mres_msg(format!(
"Could not parse the store_part of mizeid {} into a u64 for the MemStore",
id_str
))

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::item::{Item, ItemData};
use crate::platform::wasm::js_sys::Function;
use web_sys::{WorkerOptions, WorkerType};
use crate::platform::js::js_sys::Function;
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::{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::instance::module::EmptyModule;
use crate::error::MizeResult;
use crate::Mize;
use crate::MizeError;
use crate::mize_err;
// console_log macro
// that can be copied into other files for debugging purposes
#[cfg(feature = "wasm-target")]
#[cfg(feature = "target-js")]
use wasm_bindgen::prelude::*;
#[cfg(feature = "wasm-target")]
#[cfg(feature = "target-js")]
#[wasm_bindgen]
extern "C" {
#[wasm_bindgen(js_namespace = console)]
fn log(s: &str);
}
#[cfg(feature = "wasm-target")]
#[cfg(feature = "target-js")]
macro_rules! console_log {
// Note that this is using the `log` function imported above during
// `bare_bones`
($($t:tt)*) => (unsafe { log(&format_args!($($t)*).to_string())})
}
#[cfg(not(feature = "wasm-target"))]
#[cfg(not(feature = "target-js"))]
macro_rules! console_log {
// Note that this is using the `log` function imported above during
// `bare_bones`
($($t:tt)*) => ()
($($t:tt)*) => {};
}
//end of console_log macro
#[global_allocator]
static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;
pub fn wasm_instance_init(instance: &mut Instance) -> MizeResult<()> {
pub fn wasm_instance_init(mize: &mut Mize) -> MizeResult<()> {
console_log!("Hello world from wasm_instance_init!!!!!!!!!!");
#[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)?;
}
}
////// load config from MIZE_CONFIG env var
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")
}
},
};
}
Ok(())
}
pub fn load_module(instance: &mut Instance, name: &str, path: Option<String>) -> MizeResult<()> {
console_log!("loading module: {}", name);
#[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;
}
wasm_bindgen_futures::spawn_local(load_module_async(instance.clone(), name.to_owned(), path));
// 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 instance: Instance, name: String, path: Option<String>) -> () {
/*
async fn load_module_async(mut mize: Mize, name: String, path: Option<String>) -> () {
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 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_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...");
let function_str = format!(r#"
let function_str = format!(
r#"
const promise = new Promise((resolve, reject) => {{
import("{module_url}/mize_module_{name}.js").then( module => {{
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;
"#);
"#
);
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,
Err(err) => {
console_log!("failed to load the wasm mize module....");
console_log!("{:?}", err);
return;
},
}
};
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);
return;
},
}
};
let mut filled_module = unsafe {
Box::from_raw(empty_module_ptr)
};
let mut filled_module = unsafe { Box::from_raw(empty_module_ptr) };
console_log!("before....");
let a = instance.get("0/config");
//let a = mize.get("0/config");
console_log!("after....");
//filled_module.init(&instance);
/*
console_log!("got ret_result: {:?}", ret_result);
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());
}
*/
}
*/
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(".", "/");
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);
} 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));
}
}
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 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_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 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 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)
}
#[wasm_bindgen]
pub struct JsInstance {
inner: NonNull<Instance>,
#[derive(Clone)]
pub struct JsMize {
inner: NonNull<Mize>,
}
#[wasm_bindgen]
pub struct JsItemHandle {
instance: NonNull<Instance>,
pub struct JsItem {
mize: NonNull<Mize>,
id: MizeId,
}
#[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));
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 = JsInstance { inner: NonNull::from(Box::leak(Box::new(instance))) };
let mut js_instance = JsMize {
inner: NonNull::from(Box::leak(Box::new(mize))),
};
return js_instance;
}
#[wasm_bindgen]
impl JsInstance {
impl JsMize {
#[wasm_bindgen]
pub unsafe fn init(&mut self) -> MizeResult<()> {
self.inner.as_mut().init()
@@ -231,43 +287,39 @@ impl JsInstance {
}
#[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)?;
Ok(JsItemHandle { instance: self.inner, id: item.id()})
}
}
impl JsInstance {
pub fn inner(&mut self) -> &mut Instance {
unsafe {
self.inner.as_mut()
}
Ok(JsItem {
mize: self.inner,
id: item.id(),
})
}
}
impl JsMize {
pub fn inner(&mut self) -> &mut Mize {
unsafe { self.inner.as_mut() }
}
}
#[wasm_bindgen]
impl JsItemHandle {
impl JsItem {
#[wasm_bindgen]
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()?;
Ok(string)
}
#[wasm_bindgen]
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 jsvalue = serde_wasm_bindgen::to_value(data_raw.cbor())?;
Ok(jsvalue)
}
}
impl From<MizeError> for JsValue {
fn from(value: MizeError) -> Self {
let string = format!("{:?}", value);

View File

@@ -6,15 +6,15 @@ use std::path::{Path, PathBuf};
use sysinfo::{Pid, ProcessRefreshKind, RefreshKind, System};
use tracing::debug;
use crate::core::id::MizeId;
use crate::core::item::{Item, ItemData};
use crate::id::MizeId;
use crate::item::{Item, ItemData};
use crate::error::{IntoMizeResult, MizeError, MizeResult};
use crate::instance::store::IdIter;
use crate::instance::{self, Mize};
use crate::item::get_raw_from_cbor;
use crate::item::IntoItemData;
use crate::memstore::MemStore;
use crate::{core::instance::store::Store, mize_err};
use crate::{instance::store::Store, mize_err};
use tokio::net::UnixStream;
#[derive(Clone, Debug)]
@@ -82,12 +82,12 @@ async fn try_connect_to_sock(store_path: String) -> MizeResult<()> {
impl Store for FileStore {
fn new_id(&self) -> MizeResult<String> {
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 '{}'",
self.path.display()
))?)?
.parse()
.mize_result_msg(format!(
.mres_msg(format!(
"could not parse next_id at '{}' to u64",
self.path.display()
))?;
@@ -132,7 +132,7 @@ impl Store for FileStore {
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",
self.path.join("store").join(id.store_part()).display()
))?;
@@ -163,17 +163,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",
self.path.join("store").join(id.store_part()).display()
))?;
let data: ItemData = cbor_value.into_item_data();
let tmp = id.path();
let mut path_iter = tmp.into_iter();
let mut path_iter = tmp.iter();
path_iter.next();
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)?;
//println!("ret_data: {}", ret_data);
return Ok(ret_data);
}
@@ -197,12 +200,12 @@ fn valid_pid_file(path: &Path) -> MizeResult<Option<u32>> {
}
// 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 '{}'",
pid_file_path.display()
))?)?
.parse()
.mize_result_msg(format!(
.mres_msg(format!(
"Could not parse contents of pid file at '{}' to u32",
pid_file_path.display()
))?;

View File

@@ -1,86 +1,139 @@
use std::path::PathBuf;
use clap::{crate_version, Arg, Command};
use clap::{ArgAction, ArgMatches};
use clap::{Arg, crate_version, Command};
use std::collections::HashMap;
use std::path::PathBuf;
use std::sync::Arc;
use std::io::Write;
use colored::Colorize;
use std::env;
use tracing_subscriber::fmt::Subscriber;
use tracing::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 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::Event;
use tracing::{debug, error, info, trace, warn};
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 tracing::{trace, debug, info, warn, error};
use crate::id::MizeId;
use crate::item::ItemData;
use crate::MizeError;
use crate::MizeResult;
use crate::DEFAULT_LOG_LEVEL;
static DEFAULT_LOG_LEVEL: LevelFilter = LevelFilter::WARN;
pub fn init_logger(cli_matches: &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 MME".to_owned()));
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)));
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.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 MME_LOG
let mut val_mme_log = level_from_env("MME_LOG", log_level);
if let Some(level_mme_log) = val_mme_log.0 {
log_level = level_mme_log;
log_messages.push((Level::TRACE, format!("set log level from the Variable MME_LOG to {}", level_mme_log)));
// 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_mme_log.1);
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
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)));
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_lowercase().as_str() {
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,
@@ -97,38 +150,45 @@ pub fn init_logger(cli_matches: &ArgMatches) {
}
};
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;
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"))
;
//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(())
});
builder
.with_env_filter(my_env_filter)
//.with_env_filter(my_env_filter)
.without_time()
.event_format(MyFormatter)
//.with_max_level(log_level)
.fmt_fields(field_formatter)
.with_max_level(log_level)
.init();
// write out all log messages from log_messages
for (level, msg) in log_messages {
match level {
@@ -141,9 +201,14 @@ pub fn init_logger(cli_matches: &ArgMatches) {
}
}
fn level_from_env(var_name: &str, log_level: LevelFilter) -> (Option<LevelFilter>, Vec<(Level, String)>) {
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
@@ -164,15 +229,17 @@ fn level_from_env(var_name: &str, log_level: LevelFilter) -> (Option<LevelFilter
log_level
}
};
return (Some(level), log_messages)
},
Err(env::VarError::NotPresent) => {},
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("");
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: {}
@@ -180,12 +247,11 @@ fn level_from_env(var_name: &str, log_level: LevelFilter) -> (Option<LevelFilter
, 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
@@ -203,13 +269,9 @@ where
let metadata = event.metadata();
// set format
/*
builder.format(|buf, record| {
match record.level() {
}
});
*/
// 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()),
@@ -249,6 +311,43 @@ where
// Write fields on the event
ctx.field_format().format_fields(writer.by_ref(), event)?;
writeln!(writer)
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,
}
}

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

@@ -0,0 +1,467 @@
use clap::ArgMatches;
use colored::Colorize;
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::{debug, info, trace, warn};
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::GLOBAL_MIZE;
use crate::config::ConfigOpt;
use crate::error::{IntoMizeResult, MizeResult};
use crate::instance::Mize;
use crate::item::{data_from_string, IntoItemData, ItemData};
use crate::mize_err;
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(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")]
{
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())?;
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")]
{
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_part_hash(
mize: &mut Mize,
name: &str,
options: ItemData,
) -> MizeResult<(String, String)> {
let mut selector_data = mize.get("self/config/selector")?.as_data_full()?;
// 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 part_url = "https://mize.works/mize/dist/{part_hash}-{part_name}.tar.gz";
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}/{part_hash}-{part_name}.tar.gz");
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 '{}' with hash: {}", part_name, part_hash);
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 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());
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> {
// 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_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_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_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_name}.so"));
//Err(mize_err!("part {part_name} not found"))
}
pub fn load_part(mize: &mut Mize, part_name: &str, path: Option<String>) -> MizeResult<()> {
let part_path = get_part_path(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>>)>>("mize_rust_hack_set_global".as_bytes()) {
rust_hack_func(GLOBAL_MIZE.get().unwrap().clone())
};
}
let func: libloading::Symbol<
unsafe extern "C" fn() -> MizeResult<()>,
> = unsafe { lib.get(format!("part_{}", part_name).as_bytes())? };
mize.clone().set_cur_eval_part(Some(mize.clone().id_new_dots(part_name)?))?;
unsafe { func()? };
mize.clone().set_cur_eval_part(None)?;
let mut cur_eval_part = mize.cur_eval_part.lock()?;
*cur_eval_part = None;
// 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);
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

@@ -32,7 +32,7 @@ pub fn connect(instance: &mut Mize, store_path: PathBuf) -> MizeResult<()> {
let ns_of_peer_str = instance
.get(format!(
"inst/con_by_id/{}/peer/0/config/namespace",
"inst/con_by_id/{}/peer/self/config/namespace",
conn_id
))?
.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 cloned_instance = instance.clone();
instance.spawn("incomming", move || {
instance.spawn_background("incomming", move || {
let result = unix_incomming(unix_read, cloned_instance, conn_id);
// if unix incomming fails, close the connection
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();
instance.spawn("outgoing", move || {
instance.spawn_background("outgoing", move || {
let result = unix_outgoing(unix_write, send_rx, outgoing_cloned_instance, conn_id);
// if writing fails, close this connection
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
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 '{}'",
listener.sock_path.display()
))?;
@@ -100,7 +100,7 @@ async fn unix_listen(listener: UnixListener, mut instance: Mize) -> MizeResult<(
let (mut unix_sock, addr) = listener
.accept()
.await
.mize_result_msg("Error while accepting Unix sock connection")?;
.mres_msg("Error while accepting Unix sock connection")?;
info!("new connection");
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 cloned_instance = instance.clone();
instance.spawn("incomming", move || {
instance.spawn_background("incomming", move || {
let result = unix_incomming(unix_read, cloned_instance, conn_id);
// if unix incomming fails, close the connection
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();
instance.spawn("outgoing", move || {
instance.spawn_background("outgoing", move || {
let result = unix_outgoing(unix_write, send_rx, outgoing_cloned_instance, conn_id);
// if writing fails, close this connection
if let Err(err) = result {
@@ -147,7 +147,7 @@ fn unix_outgoing(
Ok(())
}
fn unix_incomming(
pub fn unix_incomming(
mut unix_read: OwnedReadHalf,
mut instance: Mize,
conn_id: u64,

View File

@@ -17,6 +17,17 @@ pub struct MizeMessage {
pub conn_id: u64,
}
impl MizeMessage {
pub fn bytes(&self) -> Vec<u8> {
let mut writer = Vec::new();
ciborium::into_writer(&self.value, &mut writer).unwrap();
writer
}
pub fn into_writer(&self, writer: &mut impl io::Write) -> MizeResult<()> {
Ok(ciborium::into_writer(&self.value, writer)?)
}
}
// msg items
static MSG_CMD: u16 = 1;
static MSG_ID: u16 = 2;
@@ -42,6 +53,7 @@ pub enum MessageCmd {
UpdateRequest,
GetSub,
Sub,
Custom(ItemData),
}
impl MizeMessage {
@@ -49,10 +61,28 @@ impl MizeMessage {
MizeMessage { value, conn_id }
}
pub fn new_custom<T: IntoItemData, U: IntoItemData>(
custom_cmd: T,
value: U,
conn_id: u64,
) -> MizeMessage {
let value = value.into_item_data();
let custom_cmd = custom_cmd.into_item_data();
let cmd = (CborValue::Integer(MSG_CMD.into()), custom_cmd.0);
let data = (CborValue::Integer(MSG_DATA.into()), value.cbor().to_owned());
let msg_value = CborValue::Map(vec![cmd, data]);
MizeMessage {
value: msg_value,
conn_id,
}
}
pub fn new_get(id: MizeId, conn_id: u64) -> MizeMessage {
let id_path = id
.path()
.into_iter()
.iter()
.map(|string| CborValue::Text(string.to_owned()))
.collect();
@@ -69,7 +99,7 @@ impl MizeMessage {
pub fn new_get_sub(id: MizeId, conn_id: u64) -> MizeMessage {
let id_path = id
.path()
.into_iter()
.iter()
.map(|string| CborValue::Text(string.to_owned()))
.collect();
@@ -86,7 +116,7 @@ impl MizeMessage {
pub fn new_sub(id: MizeId, conn_id: u64) -> MizeMessage {
let id_path = id
.path()
.into_iter()
.iter()
.map(|string| CborValue::Text(string.to_owned()))
.collect();
@@ -113,7 +143,7 @@ impl MizeMessage {
pub fn new_create_reply(id: MizeId, conn_id: u64) -> MizeMessage {
let id_path = id
.path()
.into_iter()
.iter()
.map(|string| CborValue::Text(string.to_owned()))
.collect();
@@ -130,7 +160,7 @@ impl MizeMessage {
pub fn new_give(id: MizeId, data: ItemData, conn_id: u64) -> MizeMessage {
let id_path = id
.path()
.into_iter()
.iter()
.map(|string| CborValue::Text(string.to_owned()))
.collect();
@@ -148,7 +178,7 @@ impl MizeMessage {
pub fn new_update_request(id: MizeId, data: ItemData, conn_id: u64) -> MizeMessage {
let id_path = id
.path()
.into_iter()
.iter()
.map(|string| CborValue::Text(string.to_owned()))
.collect();
@@ -166,7 +196,7 @@ impl MizeMessage {
pub fn new_update(id: MizeId, data: ItemData, conn_id: u64) -> MizeMessage {
let id_path = id
.path()
.into_iter()
.iter()
.map(|string| CborValue::Text(string.to_owned()))
.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 mut tmp_val = 0;
for (key, val) in msg_as_map {
@@ -204,6 +234,8 @@ impl MizeMessage {
if key_int == &one {
if let CborValue::Integer(val_int) = val {
tmp_val = val_int.to_owned().into();
} else {
return Ok(MessageCmd::Custom(ItemData(val.to_owned())));
}
}
}
@@ -214,7 +246,7 @@ impl MizeMessage {
tmp_val
};
// match on value of c
// match on value of cmd
let cmd = match val_of_c {
1 => MessageCmd::Get,
2 => MessageCmd::Update,
@@ -380,7 +412,7 @@ impl fmt::Display for MizeMessage {
self.value
.serialize(&mut serde_json::Serializer::pretty(display_writer))
.map_err(|serde_err| std::fmt::Error)
.mize_result_msg("serialize error")
.mres_msg("serialize error")
.inspect_err(|e| {
e.clone().log();
});

View File

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

47
deno.lock generated
View File

@@ -1,16 +1,19 @@
{
"version": "5",
"specifiers": {
"jsr:@deno/esbuild-plugin@*": "1.2.1",
"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:builtin-modules@5": "5.0.0",
"npm:esbuild@*": "0.27.3",
"npm:esbuild@~0.25.5": "0.25.12",
"npm:esbuild@~0.27.3": "0.27.3",
"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-gfm@^4.0.1": "4.0.1",
"npm:remark-parse@11": "11.0.0",
@@ -874,8 +877,8 @@
"ms@2.1.3": {
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
},
"obsidian@1.12.2_@codemirror+state@6.5.0_@codemirror+view@6.38.6": {
"integrity": "sha512-DGAzpt6vo+sMDET/o8Zj26Bj2hbHrFsNUU8TtnzCyerO/OHksMFQnU9QEnPHVsOMstt/WnHXfC56j2r1syObWg==",
"obsidian@1.12.3_@codemirror+state@6.5.0_@codemirror+view@6.38.6": {
"integrity": "sha512-HxWqe763dOqzXjnNiHmAJTRERN8KILBSqxDSEqbeSr7W8R8Jxezzbca+nz1LiiqXnMpM8lV2jzAezw3CZ4xNUw==",
"dependencies": [
"@codemirror/state",
"@codemirror/view",
@@ -992,25 +995,7 @@
},
"workspace": {
"members": {
"packages/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.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": {
"apps/obsidian-plugin": {
"dependencies": [
"jsr:@deno/esbuild-plugin@^1.2.1",
"npm:@flowershow/remark-wiki-link@^3.4.0",
@@ -1026,6 +1011,24 @@
"npm:unified@^11.0.5",
"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

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

@@ -0,0 +1,625 @@
# Mize Part System (LLM generated guide)
## Overview
Mize is built around a **parts architecture** that allows you to compose functionality modularly. Each part is a self-contained unit that can:
- Store and manage its own state
- Expose methods to other parts
- Register itself with the Mize instance
- Define configuration options
Parts are the primary way to extend Mize's functionality. The system is designed so that parts can depend on each other, and users can enable/disable parts as needed.
---
## 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

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

392
flake.nix Normal file
View File

@@ -0,0 +1,392 @@
{
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 = {
config = {
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 = let
settingsFormat = pkgs.formats.toml { };
in settingsFormat.generate "mize-build-config.toml" {
config = 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 = let
settingsFormat = pkgs.formats.toml { };
in settingsFormat.generate "mize-build-config.toml" {
config = 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]
quote = "1.0.44"
syn = "2.0.114"
proc-macro2 = "1.0"
ciborium = "0.2"

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

@@ -0,0 +1,492 @@
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()
}
fn expand_simple_include_native(simple: IncludeNativeSimple) -> TokenStream {
let name = simple.name;
let type_path = simple.type_path;
let methods = simple.methods;
let method_calls = methods.iter().map(|method| {
let method_name = method.name.to_string();
let method_ident = &method.name;
let arg_extractions = generate_arg_extraction(&method.args);
let arg_passings = generate_arg_passing(&method.args);
quote! {
.method(#method_name, |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! {
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 method_calls = methods.iter().map(|method| {
let method_name = method.name.to_string();
let method_ident = &method.name;
let arg_extractions = generate_arg_extraction(&method.args);
let arg_passings = generate_arg_passing(&method.args);
quote! {
.method(#method_name, |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 arg_extractions = generate_arg_extraction(&func.args);
let arg_passings = generate_arg_passing(&func.args);
quote! {
.method(#func_name, |args| {
#(#arg_extractions)*
let result = <#type_path>::#func_ident(#(#arg_passings),*);
Ok(mize::item::ItemData::from_native(result)?)
})
}
});
let expanded = quote! {
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)
}

148
md
View File

@@ -1,5 +1,149 @@
#!/bin/sh
set -e
if [[ "$1" == "dob" ]]; then
MIZE_SRC="/home/me/work/config/mize" # hardcoded for now
RSYNC_EXCLUDES="--exclude=node_modules --exclude=target --exclude=mize_config.toml --exclude=rust_dist --exclude=deno_dist --exclude=dist --exclude=result --exclude=pkg"
function main {
if [[ "$1" == "gen" ]]; then
generate_sources
fi
fi
if [[ "$1" == "dsp" ]]; then
deploy_spacetime
fi
if [[ "$1" == "dlu" ]]; then
deploy_lush
fi
if [[ "$1" == "dma" ]]; then
deploy_main
fi
if [[ "$1" == "dmac" ]]; then
deploy_mac
fi
if [[ "$1" == "dpw" ]]; then
deploy_ppc_website
fi
if [[ "$1" == "dmo" ]]; then
deploy_main_obsidian
fi
}
function deploy_spacetime {
spacetime publish -p $MIZE_SRC/packages/spacetime
}
function generate_sources {
echo "mize: Generating sources..."
mkdir -p $MIZE_SRC/packages/generated/src/spacetime
# Generate Cargo.toml and lib.rs
cat << 'EOF' > $MIZE_SRC/packages/generated/Cargo.toml
[package]
name = "generated"
version = "0.1.0"
edition = "2021"
[lib]
name = "generated"
path = "src/lib.rs"
[dependencies]
spacetimedb-sdk = "2.1.0"
EOF
echo -e 'pub mod spacetime;\n' > $MIZE_SRC/packages/generated/src/lib.rs
# Generate spacetime client bindings
spacetime generate -p $MIZE_SRC/packages/spacetime -o $MIZE_SRC/packages/generated/src/spacetime --lang rust
}
function deploy_main {
mkdir -p ~/here/mize/lib
cargo build --manifest-path $MIZE_SRC/packages/mize/Cargo.toml
cargo build --manifest-path $MIZE_SRC/packages/ppc/Cargo.toml --features desktop
systemctl --user stop mize
cp $MIZE_SRC/target/debug/mize ~/here/mize/
cp $MIZE_SRC/target/debug/libppc.so ~/here/mize/lib/libppc_part.so
cp $MIZE_SRC/md ~/here/mize/
cp $MIZE_SRC/target/debug/ppc ~/here/mize/
systemctl --user start mize
}
function deploy_mac {
echo "c2vi: Deploying to mac..."
# Copy files to mac
rsync -r -u -v -a --delete --include=".git/" --include=".git/**" --mkpath $RSYNC_EXCLUDES $MIZE_SRC mac:here
# compile
ssh mac << EOF
if ! tmux has-session -t ppc-deploy 2>/dev/null; then
echo "Created new tmux session...."
tmux new-session -d -s ppc-deploy "nix develop git+file:///home/me/here/mize#buildShell"
tmux send-keys -t ppc-deploy "cargo build --manifest-path /home/me/here/mize/packages/mize/Cargo.toml" Enter
tmux send-keys -t ppc-deploy "cargo build --features desktop --manifest-path /home/me/here/mize/packages/ppc/Cargo.toml" Enter
tmux send-keys -t ppc-deploy "systemctl --user restart mize" Enter
else
echo "using existing session..."
tmux send-keys -t ppc-deploy "cargo build --manifest-path /home/me/here/mize/packages/mize/Cargo.toml" Enter
tmux send-keys -t ppc-deploy "cargo build --features desktop --manifest-path /home/me/here/mize/packages/ppc/Cargo.toml" Enter
tmux send-keys -t ppc-deploy "systemctl --user restart mize" Enter
fi
EOF
}
function deploy_lush {
echo "c2vi: Deploying to lush..."
# Copy files to lush
rsync -r -u -v -a --delete --include=".git/" --include=".git/**" --mkpath $RSYNC_EXCLUDES $MIZE_SRC lush:here
# compile
ssh lush << EOF
if ! tmux has-session -t ppc-deploy 2>/dev/null; then
echo "Created new tmux session...."
tmux new-session -d -s ppc-deploy "nix develop git+file:///home/me/here/mize#buildShell"
tmux send-keys -t ppc-deploy "cargo build --manifest-path /home/me/here/mize/packages/mize/Cargo.toml" Enter
tmux send-keys -t ppc-deploy "cargo build --features desktop --manifest-path /home/me/here/mize/packages/ppc/Cargo.toml" Enter
tmux send-keys -t ppc-deploy "systemctl --user restart mize" Enter
else
echo "using existing session..."
tmux send-keys -t ppc-deploy "cargo build --manifest-path /home/me/here/mize/packages/mize/Cargo.toml" Enter
tmux send-keys -t ppc-deploy "cargo build --features desktop --manifest-path /home/me/here/mize/packages/ppc/Cargo.toml" Enter
tmux send-keys -t ppc-deploy "systemctl --user restart mize" Enter
fi
EOF
}
function deploy_ppc_website {
echo "c2vi: Deploying ppc website to fes"
# compile
cargo build --release --no-default-features --features server --manifest-path $MIZE_SRC/packages/ppc/Cargo.toml
ssh fes rm /home/server/here/ppc
# upload
scp $MIZE_SRC/target/release/ppc fes:here
# restart service
ssh fe "sudo systemctl restart ppc"
}
function deploy_main_obsidian {
echo "c2vi: Deploying to local main obsidian vault..."
deno run --config $MIZE_SRC/packages/ppc/platform/obsidian/deno.json build
cp -r $MIZE_SRC/packages/ppc/platform/obsidian/dist/* ~/work/things/.obsidian/plugins/ppc/
}
main "$@"

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,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);
}

View File

@@ -1,31 +0,0 @@
use crate::instance::MizeInstance;
#[test]
fn most_minimal_instance_usage() {
// so I'm actually starting the effort for a better code base here in the tests
// i want to write down how you would use the very important instance type
// and then implement it to make it usable in this way
// how every mize based program to run on an os should create a mize instance
// opens the instance the way the user has configured (with env vars, build args, or config
// files)
// and we just call it "new()" and not "default_system()" so that it is clear that this is the
// method that should be used
let mize = MizeInstance::new();
// then the most important things you do with your mize instance is to get/set stuff
let name: MizeItem = mize.get("1/namespace");
let id = mize.new_item();
mize.set(id.join("name"), "test-item");
// or register update callbacks and update
//mize.on_update("5/name", |delta| println!(delta.new_value()));
//mize.update("5/name", "other-item")
// the goal is that also most things about the MizeInstance itself is controlled by
// get/set/update -ing values at the 0/ paths
}

View File

@@ -1,6 +0,0 @@
[config]
namespace = "test.c2vi.dev"
test = "hello from test item"
[config.module_dir]

8841
packages/mme/Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,76 +0,0 @@
[package]
name = "mme"
version = "0.1.0"
edition = "2021"
[package.metadata.wasm-pack.profile.dev]
wasm-opt = false
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[features]
default = [ "target-os" ]
qt = [ "slint", "cpp", "qt_core", "qt_gui", "qt_widgets", "comandr/qt" ]
slint = [ "i-slint-backend-qt", "dep:slint", "slint-interpreter", "spin_on", "qt" ]
target-os= [ "tao", "wry", "tracing-subscriber", "clap", "webkit2gtk", "mize/target-os", "glib" ]
#target-os= [ "tao", "wry", "tracing-subscriber", "clap" ]
target-wasm = [ "wasm-bindgen", "console_error_panic_hook", "wasm-bindgen-cli", "web-sys", "comandr/target-wasm ", "mize/wasm-target" ]
console_error_panic_hook = ["dep:console_error_panic_hook"]
[target.'cfg(unix)'.dependencies]
webkit2gtk = { version = "2.0.1", optional = true }
[dependencies]
# core deps
colored = "2.1.0"
enum_dispatch = "0.3.13"
comandr = { git = "https://github.com/c2vi/comandr", default-features = false }
#comandr = { path = "../../../dr-comandr" }
mize = { git = "https://github.com/c2vi/mize", default-features = false }
tracing = "0.1.40"
base64 = "0.22.1"
# wasm dependencies
wasm-bindgen = { version = "0.2.84", optional = true }
console_error_panic_hook = { version = "0.1.7", optional = true }
wasm-bindgen-cli = { version = "0.2.92", optional = true }
web-sys = { version = "0.3.70", optional = true, features = ['console'] }
# os deps
tao = { version = "0.28.1", optional = true }
wry = { version = "0.41.0", optional = true, features = [ "devtools" ] }
tracing-subscriber = { version = "0.3.18", features = ["env-filter"], optional = true }
clap = { version = "4.5.8", features = ["cargo"], optional = true }
glib = { version = "0.15.12", optional = true }
# qt deps
#qt_gui = "0.5.0"
cpp = { version = "0.5.9", optional = true }
qt_gui = { version = "0.5.0", optional = true }
qt_core = { version = "0.5.0", optional = true }
qt_widgets = { version = "0.5.0", optional = true }
#slint deps
i-slint-backend-qt = { version = "1.7.1", optional = true }
slint = { version = "1.7.1", optional = true }
slint-interpreter = { version = "1.7.1", optional = true }
spin_on = { version = "0.1.1", optional = true }
custom-print = "1.0.0"
flume = "0.11.1"
ciborium = "0.2.2"
ciborium-io = { version = "0.2.2", features = ["std", "alloc"] }
wasm-bindgen-futures = "0.4.49"
[build-dependencies]
cc = "1.1.5"
cpp_build = "0.5"
[lib]
name = "mize_module_mme"
crate-type =["cdylib", "rlib"]
[target.wasm32-unknown-unknown]
rustflags = [
"-C", "link-args=--import-memory",
]

View File

@@ -1,8 +0,0 @@
set -e
wasm-pack build --features wasm-target --no-default-features
cd /home/me/work/mme-presenters/presenters/mme-js/
npm run build -- --mode development

View File

@@ -1,29 +0,0 @@
fn main() {
#[cfg(feature = "qt")]
{
let qt_lib_path = env!("MME_QT_LIB");
cc::Build::new()
.cpp(true)
.file("src/main.cpp")
.flag("-w")
.compile("mme");
println!("cargo:rustc-link-search={}", qt_lib_path);
println!("cargo:rustc-link-lib=Qt5Quick");
println!("cargo:rustc-link-lib=Qt5PrintSupport ");
println!("cargo:rustc-link-lib=Qt5Qml ");
println!("cargo:rustc-link-lib=Qt5Network ");
println!("cargo:rustc-link-lib=Qt5Widgets ");
println!("cargo:rustc-link-lib=Qt5Gui ");
println!("cargo:rustc-link-lib=Qt5Core");
println!("cargo:rustc-link-lib=Qt5WebEngine");
println!("cargo:rustc-link-lib=Qt5WebEngineCore");
println!("cargo:rustc-link-lib=Qt5WebEngineWidgets");
println!("cargo:rerun-if-changed=src/main.cpp");
cpp_build::build("src/implementors/qt_widget/mod.rs");
}
}

View File

@@ -1,19 +0,0 @@
export MIZE_MODULE_PATH=/home/me/work/mme:/home/me/work/modules:/home/me/work/mesenters
export MIZE_MODULE_NO_REPO=1
export MIZE_MODULE_NO_EXTERNALS=1
export MIZE_CONFIG=$MIZE_CONFIG:module_dir.mme=/home/me/work/mme/dist:module_dir.String=/home/me/work/modules/modules/String/dist
function dev() {
#nix develop /home/me/work/mize#modules.mme --override-input nixpkgs github:nixos/nixpkgs/ea6033ce4ddf2c0ede2ea147a450d4f880796128 --impure
nix develop /home/me/work/mize#modules.mme --override-input nixpkgs github:nixos/nixpkgs/10e687235226880ed5e9f33f1ffa71fe60f2638a --impure
#nix develop /home/me/work/mize#modules.mme --impure
}
if [[ "$1" != "no" ]]
then
dev
fi

View File

@@ -1,171 +0,0 @@
{
module = { buildMizeForSystem, mizeBuildConfig, mkMizeRustModule, hostSystem, pkgsCross, mkMizeRustShell, pkgs, pkgsNative, buildNpmPackage, mkSelString, mizeBuildConfigStr, fenix, ... }: let
selector_string = mkSelString {
modName = "mme";
};
hash = builtins.substring 0 32 (builtins.hashString "sha256" selector_string);
url = "file:///home/me/work/mme/dist/mize.js";
html = ''
<html>
<head>
<!-- script src="https://c2vi.dev/mize/wasm32-none-unknown/mize.js"></script --!>
<!-- script src="${url}"></script --!>
<script>
console.log("from script")
</script>
</head>
<body>
hello world...............
</body>
</html>
'';
in mkMizeRustModule ({
modName = "mme";
src = ./.;
cargoExtraArgs = "--no-default-features --lib";
## add the index.html
postInstall = ''
echo -n '${html}' > $out/index.html
'';
}
// (if hostSystem.name == "wasm32-none-unknown" then {
mizeBuildPhase = ''
cd $build_dir
RUSTFLAGS="-C link-args=--import-memory" RUST_LOG=off wasm-pack build --target no-modules --dev --out-dir $out -- --features wasm-target --no-default-features
echo -n 'export function get_wasm_bindgen() { return wasm_bindgen }' >> $out/mize_module_mme.js
echo -n '${html}' > $out/index.html
'';
mizeInstallPhase = "";
} else {})
// (if hostSystem.kernel.name == "linux" then builtins.trace "adding linux stuff" {
nativeBuildInputs = with pkgsCross.buildPackages; [
pkg-config
];
buildInputs = with pkgsNative; [
webkitgtk_4_1
];
} else {})
# add the devShell
// {
devShell = pkgs.mkShell {
nativeBuildInputs = with pkgs; [
#emscripten
wasm-pack
pkg-config
webkitgtk_4_1
libsForQt5.full
cmake
nasm
pkg-config
nodejs
(fenix.packages.${system}.combine [
fenix.packages.${system}.targets.wasm32-unknown-unknown.latest.toolchain
fenix.packages.${system}.latest.toolchain
fenix.packages.${system}.latest.rust-src
])
];
MIZE_BUILD_CONFIG = mizeBuildConfigStr;
buildInputs = with pkgs; [
openssl
#pango
#libsoup_3
webkitgtk_4_1
# gobject-introspection gtk4 atkmm
];
shellHook = ''
echo hiiiiiiiiiiiiii
export LD_LIBRARY_PATH=${pkgs.webkitgtk_4_1}/lib:${pkgs.libsoup_3}/lib:${pkgs.glib.out}/lib:${pkgs.gtk3}/lib:${pkgs.cairo}/lib:${pkgs.gdk-pixbuf}/lib:${pkgs.libxkbcommon}/lib:${pkgs.fontconfig.lib}/lib:${pkgs.libsForQt5.full}/lib:${pkgs.stdenv.cc.cc.lib}/lib:${pkgs.libsForQt5.qt5.qtwebengine}/lib
export CPLUS_INCLUDE_PATH=${pkgs.libsForQt5.full}/include:${pkgs.libsForQt5.qt5.qtwebengine}/include
export MME_QT_LIB=${pkgs.libsForQt5.full}/lib
# i found that this is the env war to set where QT looks for platform plugins
# at: https://forums.fedoraforum.org/showthread.php?326508-How-to-set-QT_QPA_PLATFORM_PLUGIN_PATH
export QT_QPA_PLATFORM_PLUGIN_PATH=${pkgs.libsForQt5.full}/lib/qt-5.15.14/plugins/platforms/
alias run="${./.}/run.sh"
'';
};
}
);
lib = { mkMizeModule, buildNpmPackage, pkgsCross, pkgsNative, ... }: rec {
mkMmePresenter = attrs: mkMizeModule (attrs // {
src = attrs.src;
modName = attrs.name;
select = {
mme_presenter = true;
};
});
mkMmeNpmPresenter = attrs: mkMizeModule (attrs // {
drvFunc = buildNpmPackage;
mizeBuildPhase = ''
cd $build_dir
npm i
npm run build
mv dist tmp
mkdir -p $out
mv tmp/* $out
rm -rf tmp
'';
mizeInstallPhase = "";
select = {
mme_presenter = true;
};
devShell = pkgsNative.mkShell {
buildInputs = with pkgsNative; [ nodejs_20 ];
};
});
mkMmeHtmlPresenter = attrs: mkMmePresenter ({
dontUnpack = true;
dontPath = true;
buildPhase = "";
installPhase = ''
mkdir -p $out
cp -r ${attrs.src}/* $out
'';
} // attrs);
};
externals = { fetchFromGitHub, ... }: [
(fetchFromGitHub {
owner = "c2vi";
repo = "mme-presenters";
rev = "master";
hash = "sha256-FeMBDCJBkw9XOLXC1rfedNk71uyg4OTCHaaO1jAAGto=";
})
];
}

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