Compare commits
31 Commits
562b1abe9c
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0abf4108e8 | ||
|
|
9707286375 | ||
|
|
6eedb1826a | ||
|
|
66b8713e3a | ||
|
|
37cb94d1d4 | ||
|
|
9141fc5441 | ||
|
|
2b2d40148b | ||
|
|
9078b108a6 | ||
|
|
6bd98e7c80 | ||
|
|
9e5ceaf0f9 | ||
|
|
48f7ee2e35 | ||
|
|
387c31259d | ||
|
|
7f86f3dc5e | ||
|
|
679f5e51e5 | ||
|
|
105eca862d | ||
|
|
0b316ad54f | ||
|
|
a7db7da9d2 | ||
|
|
759e6cad66 | ||
|
|
c19de1ed84 | ||
|
|
e96e93a5a0 | ||
|
|
d559159397 | ||
|
|
05d410949c | ||
|
|
b2ed9efff2 | ||
|
|
c57849f748 | ||
|
|
19573502d2 | ||
|
|
d1ddb405c8 | ||
|
|
2907d7e236 | ||
|
|
55c85ad716 | ||
|
|
193c3ce3e0 | ||
|
|
5599a48c27 | ||
|
|
c9ea571744 |
72
AGENTS.md
72
AGENTS.md
@@ -1,62 +1,24 @@
|
|||||||
# Mize
|
# Mize
|
||||||
|
|
||||||
Mize is a strongly typed "filesystem" for the age of connectivity, elevating the Unix file philosophy into modern distributed computing.
|
Mize is many things.
|
||||||
|
- a "strongly typed filesystem for the age of everyone having many devices"
|
||||||
|
- a stdlib replacement
|
||||||
|
- a framework to make malleable/extensible/configurable/debugable software fast
|
||||||
|
- a OS userspace (mainly linux but later also any other kernels)
|
||||||
|
- a library for COP = Configuration Oriented Programming
|
||||||
|
- a thing providing one single API through which any data can be interacted with
|
||||||
|
|
||||||
|
All code written for, with or as part as mize (except for the small core) is written as a Mize Part. ALWAYS referr to:
|
||||||
|
@./docs/general-agent-guide.md
|
||||||
|
@./USING-MIZE-FOR-AGENTS.md
|
||||||
|
|
||||||
|
unless instructed otherwise put new parts into `./parts/vibed.<id-of-part>`.
|
||||||
|
|
||||||
## Codebase Structure
|
## Codebase Structure
|
||||||
|
|
||||||
```
|
```
|
||||||
packages/
|
core/ # Core Mize framework (Rust)
|
||||||
├── mize/ # Core Mize framework (Rust)
|
parts/ # Mize parts (cdylibs / crates)
|
||||||
├── marts/ # Mize parts collection (Rust/TypeScript)
|
macros/ # Proc macros
|
||||||
├── mme/ # Mize Module Environment (Rust)
|
docs/ # documentation
|
||||||
├── ppc/ # Platform-specific components
|
|
||||||
├── vic/ # Victor CLI tool
|
|
||||||
└── ac_mize_macros/# Mize macros
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Key Components
|
|
||||||
|
|
||||||
To understand the main components of the architecture you canr efer to `architecture.md`.
|
|
||||||
|
|
||||||
## Require clarification and plan approval before making code changes
|
|
||||||
|
|
||||||
Before making any code changes other than the changelog, 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
|
|
||||||
|
|
||||||
### 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
|
|
||||||
|
|
||||||
### 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].
|
|
||||||
|
|||||||
6058
Cargo.lock
generated
6058
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
23
Cargo.toml
23
Cargo.toml
@@ -1,9 +1,22 @@
|
|||||||
|
|
||||||
[workspace]
|
[workspace]
|
||||||
members = [
|
members = [
|
||||||
"packages/ac_mize_macros",
|
"macros",
|
||||||
"packages/marts",
|
"apps/os",
|
||||||
"packages/mize",
|
"apps/obsidian-plugin",
|
||||||
"packages/ppc",
|
"parts/*",
|
||||||
"packages/ppc/spacetimedb",
|
"parts/mize.ui/dioxus-components",
|
||||||
|
]
|
||||||
|
exclude = [
|
||||||
|
"parts/mos",
|
||||||
|
# "parts/mme",
|
||||||
|
]
|
||||||
|
default-members = [
|
||||||
|
"apps/obsidian-plugin",
|
||||||
|
"apps/os",
|
||||||
|
"parts/*",
|
||||||
]
|
]
|
||||||
resolver = "2"
|
resolver = "2"
|
||||||
|
|
||||||
|
[workspace.dependencies]
|
||||||
|
mize = { path = "core" }
|
||||||
|
|||||||
47
README.md
47
README.md
@@ -1,25 +1,32 @@
|
|||||||
# MiZe
|
# MiZe
|
||||||
With Mize I am building the software platform, that I would want to use.
|
|
||||||
|
|
||||||
_Elevate the Unix file philosophy into the age of connectivity._
|
- website: https://mize.works
|
||||||
|
- source: https://git.ppc.social/ppc/mize
|
||||||
|
- matrix room: [#mize:ppc.social](https://matrix.to/#/#mize:ppc.social)
|
||||||
|
- matrix room for updates: [#mize-updates:ppc.social](https://matrix.to/#/#mize-updates:ppc.social)
|
||||||
|
|
||||||
## Why am I doing this?
|
## Mize is many things
|
||||||
- What annoys me about software these days is, that things are done in so many different ways.
|
- a "strongly typed filesystem for the age of everyone having many devices"
|
||||||
- e.g: Different cloud applications for syncing data (Google Cloud, OneDrive, Dropbox)
|
- "eelevate the UNIX philosophy into the age of having multiple connected devices"
|
||||||
- e.g: Every app stores the data it deals with in it's own way (the way that the developer thought is best for this particular app)
|
- a new stdlib api to replace libc (but not just for C)
|
||||||
|
- a framework to make malleable/extensible/configurable/debugable software fast
|
||||||
|
- a OS userspace (mainly linux but later also any other kernels)
|
||||||
|
- a library for COP = Configuration Oriented Programming
|
||||||
|
- a thing providing one single API through which any data from any soruce can be interacted with
|
||||||
|
- a way to produce software with agents which is not slop
|
||||||
|
- (by making humans design architecture, agents implement code)
|
||||||
|
- (and providing a solid starter set of architecture for agents to use without humans)
|
||||||
|
- ([Mize Software Factory](https://wiki.ppc.social/Mize-Software-Factory))
|
||||||
|
- [c2vi](https://c2vi.dev) tinkering on the software he thinks is solid and would want to use
|
||||||
|
|
||||||
- Back when Unix came out **everything** was stored in one place: the file system of the computer.
|
## things
|
||||||
- (just as simple bytestreams/files, organized by a folder-structure)
|
- [MiZe Parts](https://wiki.ppc.social/u/c2vi/MiZe-Part)
|
||||||
- and NO program stored and synced any data around in unpredictable ways.
|
- [MiZe Init System](https://wiki.ppc.social/u/c2vi/MiZe-Init-System)
|
||||||
|
- [Config Oriented Programming|COP = Config Oriented Programming](https://wiki.ppc.social/u/c2vi/Config-Oriented-Programming)
|
||||||
- This philosophy is still used in every Unix/Linux machine, on a local level, but completely abstracted away (especially on mobile phones) from the user (by every app).
|
- [mize lang](https://wiki.ppc.social/u/c2vi/mize-lang)
|
||||||
|
- [Mize Software Factory](https://wiki.ppc.social/Mize-Software-Factory)
|
||||||
- I would say: The Unix file system just hasn't evolved into the age of connectivity. It is still completely the same. And I think it shouldn't be.
|
|
||||||
|
|
||||||
- MiZe aims to bring that idea of storing all data
|
|
||||||
- in one place
|
|
||||||
- in one predictable and simple way
|
|
||||||
- and programs only taking data from there and not storing it everywhere in messy ways
|
|
||||||
|
|
||||||
- That's why the slogan is going to be: _Elevate the unix file philosophy into the age of connectivity._
|
|
||||||
|
|
||||||
|
- [The Idea of MiZe](https://wiki.ppc.social/u/c2vi/The-Idea-of-MiZe)
|
||||||
|
- [How the idea of MiZe came to be](https://wiki.ppc.social/u/c2vi/How-the-idea-of-MiZe-came-to-be)
|
||||||
|
- [The WHY of Mize](https://wiki.ppc.social/u/c2vi/The-WHY-of-Mize)
|
||||||
|
- [How MiZe works](https://wiki.ppc.social/u/c2vi/How-MiZe-works) (quite old article, probably no longer fully accurate)
|
||||||
|
|||||||
72
USING-MIZE-FOR-AGENTS.md
Normal file
72
USING-MIZE-FOR-AGENTS.md
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
|
||||||
|
# The Mize software factory framework.
|
||||||
|
goal: make clean/sane, configurable, extensible, **maintanable**, **human-comprehendable**, debuggable, tracable, pluggable, **well-architectured**, ... software usuing agentic engineering.
|
||||||
|
|
||||||
|
@./docs/general-agent-guide.md
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
Mize is built around the **parts architecture** which allows you to compose anc configure functionality modularly. Each part is a self-contained unit that:
|
||||||
|
|
||||||
|
- can store and manage its own state
|
||||||
|
- can expose methods to other parts
|
||||||
|
- Registers itself with the Mize instance
|
||||||
|
- Defines configuration options
|
||||||
|
- define UI components (called presenters)
|
||||||
|
|
||||||
|
Parts are the primary way to build any new software using this framework.
|
||||||
|
|
||||||
|
## conditions when to stop and check back in with the human (IMPORTANT).
|
||||||
|
- dealing with/adding dependencies on system libraries or packages is also something to stop for and ask the user how to do it.
|
||||||
|
- in case it takes too long finding smth or figuring smth out. stop and ask the user. (goal is not to do stuff as autonomous as possible)
|
||||||
|
- **Stop on architectural dead-ends.** If something does not work out architecture-wise — e.g. one part needs to notify another of a state change, but there is no elegant existing mechanism (`method` / `opt` / `has_part` / listeners / etc.) — do **not** invent a hacky workaround and push on. Stop, describe the gap and the options you see, and ask the user how to proceed.
|
||||||
|
|
||||||
|
|
||||||
|
## some other notes
|
||||||
|
- Prefer expanding an existing part over creating a new one when it fits — but ask user before expanding.
|
||||||
|
|
||||||
|
- when developing a new part. present it's config options and methods to the human first.
|
||||||
|
|
||||||
|
When turning a design / whole application into parts, follow **Splitting an app into parts**.
|
||||||
|
|
||||||
|
## Splitting an app into parts
|
||||||
|
|
||||||
|
A part is a **named capability with its own config namespace and callable surface** — not a technical layer (UI/API/DB) and not a folder dump of screens.
|
||||||
|
|
||||||
|
When turning a design document or whole application into parts:
|
||||||
|
|
||||||
|
1. **One capability, one part** — Group requirements that share one job and vocabulary (login/tokens, inbox, hub shell). That cluster becomes one part id (`auth.client`, `mme`, `sci.inbox`).
|
||||||
|
2. **Config namespace = part boundary** — Settings that naturally sit under one prefix (`auth.client.issuer`, …) belong to that part. Two unrelated prefixes → two parts.
|
||||||
|
3. **Methods are the public API** — Expose capability-shaped methods (`login`, `set_login_state`), not “whatever the dashboard clicked.” CLI and other parts should call the same `method`s via `dyn_call` / generated bindings — don’t use CLI `run` as the only inter-part API.
|
||||||
|
4. **Product / UI shells are thin** — Dashboards own layout and presentation. Domain work stays in capability parts. Optional UI notify via `has_part("…")` + `dyn_call` so headless/CLI still works.
|
||||||
|
5. **Split on replaceability or reuse** — Would you swap the IdP but keep the hub? Second app needs login without the hub? Keep that capability separate. Only this screen will ever use X? Keep X in the shell until a second consumer appears.
|
||||||
|
6. **Don’t split only by layer** — Avoid default `foo.ui` + `foo.logic`. Prefer `foo` (capability) + optional shell. Shared widgets can be a **library** (e.g. `dioxus-components`) until they need mize opts/methods.
|
||||||
|
7. **Hard vs soft dependency** — Hard: cannot run without the other (load order / deps). Soft: “if UI is loaded, push URL/code.” Soft deps must not break headless use.
|
||||||
|
8. **Lifecycle is a boundary smell** — Background refresh, blocking window, long device poll with different lifetimes often means the domain side should be its own part with clear methods.
|
||||||
|
9. **Start coarse; split on friction** — Prefer fewer parts from a design doc. Split when a second consumer appears, config namespaces diverge, or you need to enable one capability without another.
|
||||||
|
10. **Before coding, present a part table** — For each proposed part: `id`, one-sentence responsibility, `opt`s, `method`s, hard deps, soft notifies. Present that to the human first (same rule as presenting config options and methods).
|
||||||
|
|
||||||
|
|
||||||
|
## UI work
|
||||||
|
For any UI work:
|
||||||
|
- Use **Dioxus** (desktop unless the user says otherwise).
|
||||||
|
- Build with **`parts/mize.ui/dioxus-components`** — reuse existing components (`Button`, `Prompt`, `Control`, …) and extend that library when something reusable is missing; don’t invent a parallel widget set.
|
||||||
|
- Prefer **inlined CSS** via `OnceStyle` / isolated style components (see that crate). Do **not** put `document::Style` inside components that re-render often; don’t bring back `asset!` / css-modules for these components unless the user asks.
|
||||||
|
- Don’t block the Dioxus UI thread on long part work (login flows, etc.): background thread / async + part `method`s updating shared state the UI reads.
|
||||||
|
|
||||||
|
unless specified diferently any mize UI uses the PPC color pallete (derived from the PPC Logo: https://wiki.ppc.social/PPC-Logo):
|
||||||
|
```
|
||||||
|
Token Light mode Dark mode
|
||||||
|
Background #F7F8F4 #171D18
|
||||||
|
Surface #FFFFFF #242D25
|
||||||
|
Surface secondary #EFF2EA #2C362D
|
||||||
|
Primary text #20251C #F2F4EA
|
||||||
|
Secondary text #66705E #AAB5A4
|
||||||
|
Border #DFE4D9 #3A463A
|
||||||
|
Primary accent #FEC251 #D7E47D
|
||||||
|
Accent text #79500F #D7E47D
|
||||||
|
Success #526F32 #B9D987
|
||||||
|
Info #376777 #B9E4EB
|
||||||
|
Warning #A66B13 #FFD77F
|
||||||
|
Danger #B94D42 #FF9B8E
|
||||||
|
```
|
||||||
7
apps/obsidian-plugin/Cargo.toml
Normal file
7
apps/obsidian-plugin/Cargo.toml
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
[package]
|
||||||
|
name = "mize-obsidian-plugin"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
mize = { workspace = true }
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@ppc/obsidian-plugin",
|
"name": "@ppc/obsidian-plugin",
|
||||||
"tasks": {
|
"tasks": {
|
||||||
"build_rust": "wasm-pack -vvvvv build -d platform/obsidian/rust_dist --target web --no-default-features --features target-obsidian",
|
"build_rust": "CARGO_TERM_COLOR=always wasm-pack -vvvvv build -d ../apps/obsidian-plugin/rust_dist --target web --no-opt ../../marts --no-default-features --features target-obsidian",
|
||||||
"build": "deno run -A build_rust && mkdir -p dist && touch dist/.hotreload && cp manifest.json dist && deno -A esbuild.config.mjs production",
|
"build": "deno run -A build_rust && mkdir -p dist && touch dist/.hotreload && cp manifest.json dist && deno -A esbuild.config.mjs production",
|
||||||
"dev": ""
|
"dev": ""
|
||||||
},
|
},
|
||||||
@@ -1,36 +1,45 @@
|
|||||||
{
|
{
|
||||||
"version": "5",
|
"version": "5",
|
||||||
"specifiers": {
|
"specifiers": {
|
||||||
|
"jsr:@deno/esbuild-plugin@^1.2.1": "1.2.1",
|
||||||
|
"jsr:@deno/loader@~0.3.10": "0.3.12",
|
||||||
|
"jsr:@std/internal@^1.0.12": "1.0.12",
|
||||||
|
"jsr:@std/path@^1.1.1": "1.1.4",
|
||||||
"npm:@flowershow/remark-wiki-link@^3.4.0": "3.4.0",
|
"npm:@flowershow/remark-wiki-link@^3.4.0": "3.4.0",
|
||||||
"npm:@instantdb/admin@~0.21.3": "0.21.30",
|
|
||||||
"npm:@instantdb/react@~0.20.28": "0.20.28_react@19.1.0",
|
|
||||||
"npm:@react-oauth/google@~0.12.2": "0.12.2_react@19.1.0_react-dom@19.1.0__react@19.1.0",
|
|
||||||
"npm:@tailwindcss/postcss@4": "4.2.0",
|
|
||||||
"npm:@types/node@20": "20.19.33",
|
|
||||||
"npm:@types/react-dom@19": "19.2.3_@types+react@19.2.14",
|
|
||||||
"npm:@types/react@19": "19.2.14",
|
|
||||||
"npm:@wasm-tool/rollup-plugin-rust@^3.1.4": "3.1.4_binaryen@125.0.0",
|
|
||||||
"npm:binaryen@125": "125.0.0",
|
|
||||||
"npm:builtin-modules@5": "5.0.0",
|
"npm:builtin-modules@5": "5.0.0",
|
||||||
"npm:esbuild@~0.27.3": "0.27.3",
|
"npm:esbuild@~0.27.3": "0.27.3",
|
||||||
"npm:mdast@3": "3.0.0",
|
"npm:mdast@3": "3.0.0",
|
||||||
"npm:obsidian@^1.12.2": "1.12.2_@codemirror+state@6.5.0_@codemirror+view@6.38.6",
|
"npm:obsidian@^1.12.2": "1.12.2_@codemirror+state@6.5.0_@codemirror+view@6.38.6",
|
||||||
"npm:react-dom@19.1.0": "19.1.0_react@19.1.0",
|
|
||||||
"npm:react@19.1.0": "19.1.0",
|
|
||||||
"npm:remark-frontmatter@5": "5.0.0",
|
"npm:remark-frontmatter@5": "5.0.0",
|
||||||
"npm:remark-gfm@^4.0.1": "4.0.1",
|
"npm:remark-gfm@^4.0.1": "4.0.1",
|
||||||
"npm:remark-parse@11": "11.0.0",
|
"npm:remark-parse@11": "11.0.0",
|
||||||
"npm:remark-stringify@11": "11.0.0",
|
"npm:remark-stringify@11": "11.0.0",
|
||||||
"npm:tailwindcss@4": "4.2.0",
|
|
||||||
"npm:tslib@^2.6.2": "2.8.1",
|
|
||||||
"npm:typescript@^5.9.3": "5.9.3",
|
"npm:typescript@^5.9.3": "5.9.3",
|
||||||
"npm:unified@^11.0.5": "11.0.5",
|
"npm:unified@^11.0.5": "11.0.5",
|
||||||
"npm:unist-util-visit@^5.1.0": "5.1.0"
|
"npm:unist-util-visit@^5.1.0": "5.1.0"
|
||||||
},
|
},
|
||||||
"npm": {
|
"jsr": {
|
||||||
"@alloc/quick-lru@5.2.0": {
|
"@deno/esbuild-plugin@1.2.1": {
|
||||||
"integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw=="
|
"integrity": "df629467913adc1f960149fdfa3a3430ba8c20381c310fba096db244e6c3c9f6",
|
||||||
|
"dependencies": [
|
||||||
|
"jsr:@deno/loader",
|
||||||
|
"jsr:@std/path"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
|
"@deno/loader@0.3.12": {
|
||||||
|
"integrity": "52d3b3be0a32192efe07b0a4f1b3047077d2f2bba0f693e32f47421507f9fdb6"
|
||||||
|
},
|
||||||
|
"@std/internal@1.0.12": {
|
||||||
|
"integrity": "972a634fd5bc34b242024402972cd5143eac68d8dffaca5eaa4dba30ce17b027"
|
||||||
|
},
|
||||||
|
"@std/path@1.1.4": {
|
||||||
|
"integrity": "1d2d43f39efb1b42f0b1882a25486647cb851481862dc7313390b2bb044314b5",
|
||||||
|
"dependencies": [
|
||||||
|
"jsr:@std/internal"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"npm": {
|
||||||
"@codemirror/state@6.5.0": {
|
"@codemirror/state@6.5.0": {
|
||||||
"integrity": "sha512-MwBHVK60IiIHDcoMet78lxt6iw5gJOGSbNbOIVBHWVXIH4/Nq1+GQgLLGgI1KlnN86WDXsPudVaqYHKBIx7Eyw==",
|
"integrity": "sha512-MwBHVK60IiIHDcoMet78lxt6iw5gJOGSbNbOIVBHWVXIH4/Nq1+GQgLLGgI1KlnN86WDXsPudVaqYHKBIx7Eyw==",
|
||||||
"dependencies": [
|
"dependencies": [
|
||||||
@@ -185,196 +194,9 @@
|
|||||||
"micromark-util-symbol"
|
"micromark-util-symbol"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"@iarna/toml@2.2.5": {
|
|
||||||
"integrity": "sha512-trnsAYxU3xnS1gPHPyU961coFyLkh4gAD/0zQ5mymY4yOZ+CYvsPqUbOFSw0aDM4y0tV7tiFxL/1XfXPNC6IPg=="
|
|
||||||
},
|
|
||||||
"@instantdb/admin@0.21.30": {
|
|
||||||
"integrity": "sha512-IR8Wi+wJkGVqClJUnEFkUXYScyhmUV1qPO4s1+eHfFptkquHHtGSz1bUffGbGZJCj84lJVpjcvS1MCPgvd2PBg==",
|
|
||||||
"dependencies": [
|
|
||||||
"@instantdb/core@0.21.30",
|
|
||||||
"@instantdb/version@0.21.30",
|
|
||||||
"eventsource"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"@instantdb/core@0.20.28": {
|
|
||||||
"integrity": "sha512-0ePAgbOZedSLZ4LbPZL6afcFnhL5mWIoWXgOqzdmp6QUrYgtQjWrJCcFlA7DWmrkidZYLU2WVpQbhMbgzKqrOg==",
|
|
||||||
"dependencies": [
|
|
||||||
"@instantdb/version@0.20.28",
|
|
||||||
"mutative",
|
|
||||||
"uuid"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"@instantdb/core@0.21.30": {
|
|
||||||
"integrity": "sha512-PB/QFtrOYRmjKDYzo7oOIuJalLJVY/4mLljcYFkrbShMKz1oukKQ8sy+0/9pVcUETMyePgJUmuXcnrHwO7xjgQ==",
|
|
||||||
"dependencies": [
|
|
||||||
"@instantdb/version@0.21.30",
|
|
||||||
"mutative",
|
|
||||||
"uuid"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"@instantdb/react@0.20.28_react@19.1.0": {
|
|
||||||
"integrity": "sha512-wPig+jUeUqSCYGdCLIGVky8bL0h3VnjYYD2+5SZtF/KlgFGQZ+u8LCdvk+8QJL/kE0xqDbogr7D+b7Vn73uO/A==",
|
|
||||||
"dependencies": [
|
|
||||||
"@instantdb/core@0.20.28",
|
|
||||||
"@instantdb/version@0.20.28",
|
|
||||||
"react"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"@instantdb/version@0.20.28": {
|
|
||||||
"integrity": "sha512-lwsF66TpVwfBOSQ+bpuS2K1NO7jwlnJS5Uh4Xhyylwu4ieOEOOZa2vMnVORsidZCq2vwwKEiLvFIoH8lVkrj0g=="
|
|
||||||
},
|
|
||||||
"@instantdb/version@0.21.30": {
|
|
||||||
"integrity": "sha512-yhrxnZo3c2/EK3dnDEcX4XYHfXjlyj3QPPvylrBY2qxodKzcEh9BnJufiQiP+2ePUD+kET+2FQlztxQ4E9BAbg=="
|
|
||||||
},
|
|
||||||
"@isaacs/fs-minipass@4.0.1": {
|
|
||||||
"integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==",
|
|
||||||
"dependencies": [
|
|
||||||
"minipass"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"@jridgewell/gen-mapping@0.3.13": {
|
|
||||||
"integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==",
|
|
||||||
"dependencies": [
|
|
||||||
"@jridgewell/sourcemap-codec",
|
|
||||||
"@jridgewell/trace-mapping"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"@jridgewell/remapping@2.3.5": {
|
|
||||||
"integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==",
|
|
||||||
"dependencies": [
|
|
||||||
"@jridgewell/gen-mapping",
|
|
||||||
"@jridgewell/trace-mapping"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"@jridgewell/resolve-uri@3.1.2": {
|
|
||||||
"integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="
|
|
||||||
},
|
|
||||||
"@jridgewell/sourcemap-codec@1.5.5": {
|
|
||||||
"integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="
|
|
||||||
},
|
|
||||||
"@jridgewell/trace-mapping@0.3.31": {
|
|
||||||
"integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==",
|
|
||||||
"dependencies": [
|
|
||||||
"@jridgewell/resolve-uri",
|
|
||||||
"@jridgewell/sourcemap-codec"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"@marijn/find-cluster-break@1.0.2": {
|
"@marijn/find-cluster-break@1.0.2": {
|
||||||
"integrity": "sha512-l0h88YhZFyKdXIFNfSWpyjStDjGHwZ/U7iobcK1cQQD8sejsONdQtTVU+1wVN1PBw40PiiHB1vA5S7VTfQiP9g=="
|
"integrity": "sha512-l0h88YhZFyKdXIFNfSWpyjStDjGHwZ/U7iobcK1cQQD8sejsONdQtTVU+1wVN1PBw40PiiHB1vA5S7VTfQiP9g=="
|
||||||
},
|
},
|
||||||
"@react-oauth/google@0.12.2_react@19.1.0_react-dom@19.1.0__react@19.1.0": {
|
|
||||||
"integrity": "sha512-d1GVm2uD4E44EJft2RbKtp8Z1fp/gK8Lb6KHgs3pHlM0PxCXGLaq8LLYQYENnN4xPWO1gkL4apBtlPKzpLvZwg==",
|
|
||||||
"dependencies": [
|
|
||||||
"react",
|
|
||||||
"react-dom"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"@rollup/pluginutils@5.3.0": {
|
|
||||||
"integrity": "sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q==",
|
|
||||||
"dependencies": [
|
|
||||||
"@types/estree",
|
|
||||||
"estree-walker",
|
|
||||||
"picomatch"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"@tailwindcss/node@4.2.0": {
|
|
||||||
"integrity": "sha512-Yv+fn/o2OmL5fh/Ir62VXItdShnUxfpkMA4Y7jdeC8O81WPB8Kf6TT6GSHvnqgSwDzlB5iT7kDpeXxLsUS0T6Q==",
|
|
||||||
"dependencies": [
|
|
||||||
"@jridgewell/remapping",
|
|
||||||
"enhanced-resolve",
|
|
||||||
"jiti",
|
|
||||||
"lightningcss",
|
|
||||||
"magic-string",
|
|
||||||
"source-map-js",
|
|
||||||
"tailwindcss"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"@tailwindcss/oxide-android-arm64@4.2.0": {
|
|
||||||
"integrity": "sha512-F0QkHAVaW/JNBWl4CEKWdZ9PMb0khw5DCELAOnu+RtjAfx5Zgw+gqCHFvqg3AirU1IAd181fwOtJQ5I8Yx5wtw==",
|
|
||||||
"os": ["android"],
|
|
||||||
"cpu": ["arm64"]
|
|
||||||
},
|
|
||||||
"@tailwindcss/oxide-darwin-arm64@4.2.0": {
|
|
||||||
"integrity": "sha512-I0QylkXsBsJMZ4nkUNSR04p6+UptjcwhcVo3Zu828ikiEqHjVmQL9RuQ6uT/cVIiKpvtVA25msu/eRV97JeNSA==",
|
|
||||||
"os": ["darwin"],
|
|
||||||
"cpu": ["arm64"]
|
|
||||||
},
|
|
||||||
"@tailwindcss/oxide-darwin-x64@4.2.0": {
|
|
||||||
"integrity": "sha512-6TmQIn4p09PBrmnkvbYQ0wbZhLtbaksCDx7Y7R3FYYx0yxNA7xg5KP7dowmQ3d2JVdabIHvs3Hx4K3d5uCf8xg==",
|
|
||||||
"os": ["darwin"],
|
|
||||||
"cpu": ["x64"]
|
|
||||||
},
|
|
||||||
"@tailwindcss/oxide-freebsd-x64@4.2.0": {
|
|
||||||
"integrity": "sha512-qBudxDvAa2QwGlq9y7VIzhTvp2mLJ6nD/G8/tI70DCDoneaUeLWBJaPcbfzqRIWraj+o969aDQKvKW9dvkUizw==",
|
|
||||||
"os": ["freebsd"],
|
|
||||||
"cpu": ["x64"]
|
|
||||||
},
|
|
||||||
"@tailwindcss/oxide-linux-arm-gnueabihf@4.2.0": {
|
|
||||||
"integrity": "sha512-7XKkitpy5NIjFZNUQPeUyNJNJn1CJeV7rmMR+exHfTuOsg8rxIO9eNV5TSEnqRcaOK77zQpsyUkBWmPy8FgdSg==",
|
|
||||||
"os": ["linux"],
|
|
||||||
"cpu": ["arm"]
|
|
||||||
},
|
|
||||||
"@tailwindcss/oxide-linux-arm64-gnu@4.2.0": {
|
|
||||||
"integrity": "sha512-Mff5a5Q3WoQR01pGU1gr29hHM1N93xYrKkGXfPw/aRtK4bOc331Ho4Tgfsm5WDGvpevqMpdlkCojT3qlCQbCpA==",
|
|
||||||
"os": ["linux"],
|
|
||||||
"cpu": ["arm64"]
|
|
||||||
},
|
|
||||||
"@tailwindcss/oxide-linux-arm64-musl@4.2.0": {
|
|
||||||
"integrity": "sha512-XKcSStleEVnbH6W/9DHzZv1YhjE4eSS6zOu2eRtYAIh7aV4o3vIBs+t/B15xlqoxt6ef/0uiqJVB6hkHjWD/0A==",
|
|
||||||
"os": ["linux"],
|
|
||||||
"cpu": ["arm64"]
|
|
||||||
},
|
|
||||||
"@tailwindcss/oxide-linux-x64-gnu@4.2.0": {
|
|
||||||
"integrity": "sha512-/hlXCBqn9K6fi7eAM0RsobHwJYa5V/xzWspVTzxnX+Ft9v6n+30Pz8+RxCn7sQL/vRHHLS30iQPrHQunu6/vJA==",
|
|
||||||
"os": ["linux"],
|
|
||||||
"cpu": ["x64"]
|
|
||||||
},
|
|
||||||
"@tailwindcss/oxide-linux-x64-musl@4.2.0": {
|
|
||||||
"integrity": "sha512-lKUaygq4G7sWkhQbfdRRBkaq4LY39IriqBQ+Gk6l5nKq6Ay2M2ZZb1tlIyRNgZKS8cbErTwuYSor0IIULC0SHw==",
|
|
||||||
"os": ["linux"],
|
|
||||||
"cpu": ["x64"]
|
|
||||||
},
|
|
||||||
"@tailwindcss/oxide-wasm32-wasi@4.2.0": {
|
|
||||||
"integrity": "sha512-xuDjhAsFdUuFP5W9Ze4k/o4AskUtI8bcAGU4puTYprr89QaYFmhYOPfP+d1pH+k9ets6RoE23BXZM1X1jJqoyw==",
|
|
||||||
"cpu": ["wasm32"]
|
|
||||||
},
|
|
||||||
"@tailwindcss/oxide-win32-arm64-msvc@4.2.0": {
|
|
||||||
"integrity": "sha512-2UU/15y1sWDEDNJXxEIrfWKC2Yb4YgIW5Xz2fKFqGzFWfoMHWFlfa1EJlGO2Xzjkq/tvSarh9ZTjvbxqWvLLXA==",
|
|
||||||
"os": ["win32"],
|
|
||||||
"cpu": ["arm64"]
|
|
||||||
},
|
|
||||||
"@tailwindcss/oxide-win32-x64-msvc@4.2.0": {
|
|
||||||
"integrity": "sha512-CrFadmFoc+z76EV6LPG1jx6XceDsaCG3lFhyLNo/bV9ByPrE+FnBPckXQVP4XRkN76h3Fjt/a+5Er/oA/nCBvQ==",
|
|
||||||
"os": ["win32"],
|
|
||||||
"cpu": ["x64"]
|
|
||||||
},
|
|
||||||
"@tailwindcss/oxide@4.2.0": {
|
|
||||||
"integrity": "sha512-AZqQzADaj742oqn2xjl5JbIOzZB/DGCYF/7bpvhA8KvjUj9HJkag6bBuwZvH1ps6dfgxNHyuJVlzSr2VpMgdTQ==",
|
|
||||||
"optionalDependencies": [
|
|
||||||
"@tailwindcss/oxide-android-arm64",
|
|
||||||
"@tailwindcss/oxide-darwin-arm64",
|
|
||||||
"@tailwindcss/oxide-darwin-x64",
|
|
||||||
"@tailwindcss/oxide-freebsd-x64",
|
|
||||||
"@tailwindcss/oxide-linux-arm-gnueabihf",
|
|
||||||
"@tailwindcss/oxide-linux-arm64-gnu",
|
|
||||||
"@tailwindcss/oxide-linux-arm64-musl",
|
|
||||||
"@tailwindcss/oxide-linux-x64-gnu",
|
|
||||||
"@tailwindcss/oxide-linux-x64-musl",
|
|
||||||
"@tailwindcss/oxide-wasm32-wasi",
|
|
||||||
"@tailwindcss/oxide-win32-arm64-msvc",
|
|
||||||
"@tailwindcss/oxide-win32-x64-msvc"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"@tailwindcss/postcss@4.2.0": {
|
|
||||||
"integrity": "sha512-u6YBacGpOm/ixPfKqfgrJEjMfrYmPD7gEFRoygS/hnQaRtV0VCBdpkx5Ouw9pnaLRwwlgGCuJw8xLpaR0hOrQg==",
|
|
||||||
"dependencies": [
|
|
||||||
"@alloc/quick-lru",
|
|
||||||
"@tailwindcss/node",
|
|
||||||
"@tailwindcss/oxide",
|
|
||||||
"postcss",
|
|
||||||
"tailwindcss"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"@types/codemirror@5.60.8": {
|
"@types/codemirror@5.60.8": {
|
||||||
"integrity": "sha512-VjFgDF/eB+Aklcy15TtOTLQeMjTo07k7KAjql8OK5Dirr7a6sJY4T1uVBDuTVG9VEmn1uUsohOpYnVfgC6/jyw==",
|
"integrity": "sha512-VjFgDF/eB+Aklcy15TtOTLQeMjTo07k7KAjql8OK5Dirr7a6sJY4T1uVBDuTVG9VEmn1uUsohOpYnVfgC6/jyw==",
|
||||||
"dependencies": [
|
"dependencies": [
|
||||||
@@ -399,24 +221,6 @@
|
|||||||
"@types/ms@2.1.0": {
|
"@types/ms@2.1.0": {
|
||||||
"integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA=="
|
"integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA=="
|
||||||
},
|
},
|
||||||
"@types/node@20.19.33": {
|
|
||||||
"integrity": "sha512-Rs1bVAIdBs5gbTIKza/tgpMuG1k3U/UMJLWecIMxNdJFDMzcM5LOiLVRYh3PilWEYDIeUDv7bpiHPLPsbydGcw==",
|
|
||||||
"dependencies": [
|
|
||||||
"undici-types"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"@types/react-dom@19.2.3_@types+react@19.2.14": {
|
|
||||||
"integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==",
|
|
||||||
"dependencies": [
|
|
||||||
"@types/react"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"@types/react@19.2.14": {
|
|
||||||
"integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==",
|
|
||||||
"dependencies": [
|
|
||||||
"csstype"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"@types/tern@0.23.9": {
|
"@types/tern@0.23.9": {
|
||||||
"integrity": "sha512-ypzHFE/wBzh+BlH6rrBgS5I/Z7RD21pGhZ2rltb/+ZrVM1awdZwjx7hE5XfuYgHWk9uvV5HLZN3SloevCAp3Bw==",
|
"integrity": "sha512-ypzHFE/wBzh+BlH6rrBgS5I/Z7RD21pGhZ2rltb/+ZrVM1awdZwjx7hE5XfuYgHWk9uvV5HLZN3SloevCAp3Bw==",
|
||||||
"dependencies": [
|
"dependencies": [
|
||||||
@@ -426,59 +230,21 @@
|
|||||||
"@types/unist@3.0.3": {
|
"@types/unist@3.0.3": {
|
||||||
"integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q=="
|
"integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q=="
|
||||||
},
|
},
|
||||||
"@wasm-tool/rollup-plugin-rust@3.1.4_binaryen@125.0.0": {
|
|
||||||
"integrity": "sha512-XW3nk8yFPPc86jLXVEXkqQxsx9M6i+j5Boru5yapfS/+egK2IOYsuEOvM3am+/YrKczohKP45+cBKYkxSSGGzg==",
|
|
||||||
"dependencies": [
|
|
||||||
"@iarna/toml",
|
|
||||||
"@rollup/pluginutils",
|
|
||||||
"binaryen",
|
|
||||||
"chalk",
|
|
||||||
"glob",
|
|
||||||
"node-fetch",
|
|
||||||
"rimraf",
|
|
||||||
"tar"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"bail@2.0.2": {
|
"bail@2.0.2": {
|
||||||
"integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw=="
|
"integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw=="
|
||||||
},
|
},
|
||||||
"balanced-match@4.0.3": {
|
|
||||||
"integrity": "sha512-1pHv8LX9CpKut1Zp4EXey7Z8OfH11ONNH6Dhi2WDUt31VVZFXZzKwXcysBgqSumFCmR+0dqjMK5v5JiFHzi0+g=="
|
|
||||||
},
|
|
||||||
"binaryen@125.0.0": {
|
|
||||||
"integrity": "sha512-X7CUM9ZnwL/Ow++JH5AJKiemc82J7JyeryuPvXQdXBLcL/rqrC5KMUB1mHiORSolietH9sotvaOZlr6HSwPAlw==",
|
|
||||||
"bin": true
|
|
||||||
},
|
|
||||||
"brace-expansion@5.0.2": {
|
|
||||||
"integrity": "sha512-Pdk8c9poy+YhOgVWw1JNN22/HcivgKWwpxKq04M/jTmHyCZn12WPJebZxdjSa5TmBqISrUSgNYU3eRORljfCCw==",
|
|
||||||
"dependencies": [
|
|
||||||
"balanced-match"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"builtin-modules@5.0.0": {
|
"builtin-modules@5.0.0": {
|
||||||
"integrity": "sha512-bkXY9WsVpY7CvMhKSR6pZilZu9Ln5WDrKVBUXf2S443etkmEO4V58heTecXcUIsNsi4Rx8JUO4NfX1IcQl4deg=="
|
"integrity": "sha512-bkXY9WsVpY7CvMhKSR6pZilZu9Ln5WDrKVBUXf2S443etkmEO4V58heTecXcUIsNsi4Rx8JUO4NfX1IcQl4deg=="
|
||||||
},
|
},
|
||||||
"ccount@2.0.1": {
|
"ccount@2.0.1": {
|
||||||
"integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg=="
|
"integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg=="
|
||||||
},
|
},
|
||||||
"chalk@5.6.2": {
|
|
||||||
"integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA=="
|
|
||||||
},
|
|
||||||
"character-entities@2.0.2": {
|
"character-entities@2.0.2": {
|
||||||
"integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ=="
|
"integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ=="
|
||||||
},
|
},
|
||||||
"chownr@3.0.0": {
|
|
||||||
"integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g=="
|
|
||||||
},
|
|
||||||
"crelt@1.0.6": {
|
"crelt@1.0.6": {
|
||||||
"integrity": "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g=="
|
"integrity": "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g=="
|
||||||
},
|
},
|
||||||
"csstype@3.2.3": {
|
|
||||||
"integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ=="
|
|
||||||
},
|
|
||||||
"data-uri-to-buffer@4.0.1": {
|
|
||||||
"integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A=="
|
|
||||||
},
|
|
||||||
"debug@4.4.3": {
|
"debug@4.4.3": {
|
||||||
"integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
|
"integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
|
||||||
"dependencies": [
|
"dependencies": [
|
||||||
@@ -494,22 +260,12 @@
|
|||||||
"dequal@2.0.3": {
|
"dequal@2.0.3": {
|
||||||
"integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA=="
|
"integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA=="
|
||||||
},
|
},
|
||||||
"detect-libc@2.1.2": {
|
|
||||||
"integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="
|
|
||||||
},
|
|
||||||
"devlop@1.1.0": {
|
"devlop@1.1.0": {
|
||||||
"integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==",
|
"integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==",
|
||||||
"dependencies": [
|
"dependencies": [
|
||||||
"dequal"
|
"dequal"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"enhanced-resolve@5.19.0": {
|
|
||||||
"integrity": "sha512-phv3E1Xl4tQOShqSte26C7Fl84EwUdZsyOuSSk9qtAGyyQs2s3jJzComh+Abf4g187lUUAvH+H26omrqia2aGg==",
|
|
||||||
"dependencies": [
|
|
||||||
"graceful-fs",
|
|
||||||
"tapable"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"esbuild@0.27.3": {
|
"esbuild@0.27.3": {
|
||||||
"integrity": "sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==",
|
"integrity": "sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==",
|
||||||
"optionalDependencies": [
|
"optionalDependencies": [
|
||||||
@@ -546,18 +302,6 @@
|
|||||||
"escape-string-regexp@5.0.0": {
|
"escape-string-regexp@5.0.0": {
|
||||||
"integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw=="
|
"integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw=="
|
||||||
},
|
},
|
||||||
"estree-walker@2.0.2": {
|
|
||||||
"integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="
|
|
||||||
},
|
|
||||||
"eventsource-parser@3.0.6": {
|
|
||||||
"integrity": "sha512-Vo1ab+QXPzZ4tCa8SwIHJFaSzy4R6SHf7BY79rFBDf0idraZWAkYrDjDj8uWaSm3S2TK+hJ7/t1CEmZ7jXw+pg=="
|
|
||||||
},
|
|
||||||
"eventsource@4.1.0": {
|
|
||||||
"integrity": "sha512-2GuF51iuHX6A9xdTccMTsNb7VO0lHZihApxhvQzJB5A03DvHDd2FQepodbMaztPBmBcE/ox7o2gqaxGhYB9LhQ==",
|
|
||||||
"dependencies": [
|
|
||||||
"eventsource-parser"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"extend@3.0.2": {
|
"extend@3.0.2": {
|
||||||
"integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="
|
"integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="
|
||||||
},
|
},
|
||||||
@@ -567,129 +311,18 @@
|
|||||||
"format"
|
"format"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"fetch-blob@3.2.0": {
|
|
||||||
"integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==",
|
|
||||||
"dependencies": [
|
|
||||||
"node-domexception",
|
|
||||||
"web-streams-polyfill"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"format@0.2.2": {
|
"format@0.2.2": {
|
||||||
"integrity": "sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww=="
|
"integrity": "sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww=="
|
||||||
},
|
},
|
||||||
"formdata-polyfill@4.0.10": {
|
|
||||||
"integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==",
|
|
||||||
"dependencies": [
|
|
||||||
"fetch-blob"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"github-slugger@2.0.0": {
|
"github-slugger@2.0.0": {
|
||||||
"integrity": "sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw=="
|
"integrity": "sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw=="
|
||||||
},
|
},
|
||||||
"glob@13.0.6": {
|
|
||||||
"integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==",
|
|
||||||
"dependencies": [
|
|
||||||
"minimatch",
|
|
||||||
"minipass",
|
|
||||||
"path-scurry"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"graceful-fs@4.2.11": {
|
|
||||||
"integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="
|
|
||||||
},
|
|
||||||
"is-plain-obj@4.1.0": {
|
"is-plain-obj@4.1.0": {
|
||||||
"integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg=="
|
"integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg=="
|
||||||
},
|
},
|
||||||
"jiti@2.6.1": {
|
|
||||||
"integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==",
|
|
||||||
"bin": true
|
|
||||||
},
|
|
||||||
"lightningcss-android-arm64@1.31.1": {
|
|
||||||
"integrity": "sha512-HXJF3x8w9nQ4jbXRiNppBCqeZPIAfUo8zE/kOEGbW5NZvGc/K7nMxbhIr+YlFlHW5mpbg/YFPdbnCh1wAXCKFg==",
|
|
||||||
"os": ["android"],
|
|
||||||
"cpu": ["arm64"]
|
|
||||||
},
|
|
||||||
"lightningcss-darwin-arm64@1.31.1": {
|
|
||||||
"integrity": "sha512-02uTEqf3vIfNMq3h/z2cJfcOXnQ0GRwQrkmPafhueLb2h7mqEidiCzkE4gBMEH65abHRiQvhdcQ+aP0D0g67sg==",
|
|
||||||
"os": ["darwin"],
|
|
||||||
"cpu": ["arm64"]
|
|
||||||
},
|
|
||||||
"lightningcss-darwin-x64@1.31.1": {
|
|
||||||
"integrity": "sha512-1ObhyoCY+tGxtsz1lSx5NXCj3nirk0Y0kB/g8B8DT+sSx4G9djitg9ejFnjb3gJNWo7qXH4DIy2SUHvpoFwfTA==",
|
|
||||||
"os": ["darwin"],
|
|
||||||
"cpu": ["x64"]
|
|
||||||
},
|
|
||||||
"lightningcss-freebsd-x64@1.31.1": {
|
|
||||||
"integrity": "sha512-1RINmQKAItO6ISxYgPwszQE1BrsVU5aB45ho6O42mu96UiZBxEXsuQ7cJW4zs4CEodPUioj/QrXW1r9pLUM74A==",
|
|
||||||
"os": ["freebsd"],
|
|
||||||
"cpu": ["x64"]
|
|
||||||
},
|
|
||||||
"lightningcss-linux-arm-gnueabihf@1.31.1": {
|
|
||||||
"integrity": "sha512-OOCm2//MZJ87CdDK62rZIu+aw9gBv4azMJuA8/KB74wmfS3lnC4yoPHm0uXZ/dvNNHmnZnB8XLAZzObeG0nS1g==",
|
|
||||||
"os": ["linux"],
|
|
||||||
"cpu": ["arm"]
|
|
||||||
},
|
|
||||||
"lightningcss-linux-arm64-gnu@1.31.1": {
|
|
||||||
"integrity": "sha512-WKyLWztD71rTnou4xAD5kQT+982wvca7E6QoLpoawZ1gP9JM0GJj4Tp5jMUh9B3AitHbRZ2/H3W5xQmdEOUlLg==",
|
|
||||||
"os": ["linux"],
|
|
||||||
"cpu": ["arm64"]
|
|
||||||
},
|
|
||||||
"lightningcss-linux-arm64-musl@1.31.1": {
|
|
||||||
"integrity": "sha512-mVZ7Pg2zIbe3XlNbZJdjs86YViQFoJSpc41CbVmKBPiGmC4YrfeOyz65ms2qpAobVd7WQsbW4PdsSJEMymyIMg==",
|
|
||||||
"os": ["linux"],
|
|
||||||
"cpu": ["arm64"]
|
|
||||||
},
|
|
||||||
"lightningcss-linux-x64-gnu@1.31.1": {
|
|
||||||
"integrity": "sha512-xGlFWRMl+0KvUhgySdIaReQdB4FNudfUTARn7q0hh/V67PVGCs3ADFjw+6++kG1RNd0zdGRlEKa+T13/tQjPMA==",
|
|
||||||
"os": ["linux"],
|
|
||||||
"cpu": ["x64"]
|
|
||||||
},
|
|
||||||
"lightningcss-linux-x64-musl@1.31.1": {
|
|
||||||
"integrity": "sha512-eowF8PrKHw9LpoZii5tdZwnBcYDxRw2rRCyvAXLi34iyeYfqCQNA9rmUM0ce62NlPhCvof1+9ivRaTY6pSKDaA==",
|
|
||||||
"os": ["linux"],
|
|
||||||
"cpu": ["x64"]
|
|
||||||
},
|
|
||||||
"lightningcss-win32-arm64-msvc@1.31.1": {
|
|
||||||
"integrity": "sha512-aJReEbSEQzx1uBlQizAOBSjcmr9dCdL3XuC/6HLXAxmtErsj2ICo5yYggg1qOODQMtnjNQv2UHb9NpOuFtYe4w==",
|
|
||||||
"os": ["win32"],
|
|
||||||
"cpu": ["arm64"]
|
|
||||||
},
|
|
||||||
"lightningcss-win32-x64-msvc@1.31.1": {
|
|
||||||
"integrity": "sha512-I9aiFrbd7oYHwlnQDqr1Roz+fTz61oDDJX7n9tYF9FJymH1cIN1DtKw3iYt6b8WZgEjoNwVSncwF4wx/ZedMhw==",
|
|
||||||
"os": ["win32"],
|
|
||||||
"cpu": ["x64"]
|
|
||||||
},
|
|
||||||
"lightningcss@1.31.1": {
|
|
||||||
"integrity": "sha512-l51N2r93WmGUye3WuFoN5k10zyvrVs0qfKBhyC5ogUQ6Ew6JUSswh78mbSO+IU3nTWsyOArqPCcShdQSadghBQ==",
|
|
||||||
"dependencies": [
|
|
||||||
"detect-libc"
|
|
||||||
],
|
|
||||||
"optionalDependencies": [
|
|
||||||
"lightningcss-android-arm64",
|
|
||||||
"lightningcss-darwin-arm64",
|
|
||||||
"lightningcss-darwin-x64",
|
|
||||||
"lightningcss-freebsd-x64",
|
|
||||||
"lightningcss-linux-arm-gnueabihf",
|
|
||||||
"lightningcss-linux-arm64-gnu",
|
|
||||||
"lightningcss-linux-arm64-musl",
|
|
||||||
"lightningcss-linux-x64-gnu",
|
|
||||||
"lightningcss-linux-x64-musl",
|
|
||||||
"lightningcss-win32-arm64-msvc",
|
|
||||||
"lightningcss-win32-x64-msvc"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"longest-streak@3.1.0": {
|
"longest-streak@3.1.0": {
|
||||||
"integrity": "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g=="
|
"integrity": "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g=="
|
||||||
},
|
},
|
||||||
"lru-cache@11.2.6": {
|
|
||||||
"integrity": "sha512-ESL2CrkS/2wTPfuend7Zhkzo2u0daGJ/A2VucJOgQ/C48S/zB8MMeMHSGKYpXhIjbPxfuezITkaBH1wqv00DDQ=="
|
|
||||||
},
|
|
||||||
"magic-string@0.30.21": {
|
|
||||||
"integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==",
|
|
||||||
"dependencies": [
|
|
||||||
"@jridgewell/sourcemap-codec"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"markdown-table@3.0.4": {
|
"markdown-table@3.0.4": {
|
||||||
"integrity": "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw=="
|
"integrity": "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw=="
|
||||||
},
|
},
|
||||||
@@ -1070,46 +703,12 @@
|
|||||||
"micromark-util-types"
|
"micromark-util-types"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"minimatch@10.2.2": {
|
|
||||||
"integrity": "sha512-+G4CpNBxa5MprY+04MbgOw1v7So6n5JY166pFi9KfYwT78fxScCeSNQSNzp6dpPSW2rONOps6Ocam1wFhCgoVw==",
|
|
||||||
"dependencies": [
|
|
||||||
"brace-expansion"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"minipass@7.1.3": {
|
|
||||||
"integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A=="
|
|
||||||
},
|
|
||||||
"minizlib@3.1.0": {
|
|
||||||
"integrity": "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==",
|
|
||||||
"dependencies": [
|
|
||||||
"minipass"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"moment@2.29.4": {
|
"moment@2.29.4": {
|
||||||
"integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w=="
|
"integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w=="
|
||||||
},
|
},
|
||||||
"ms@2.1.3": {
|
"ms@2.1.3": {
|
||||||
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
|
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
|
||||||
},
|
},
|
||||||
"mutative@1.3.0": {
|
|
||||||
"integrity": "sha512-8MJj6URmOZAV70dpFe1YnSppRTKC4DsMkXQiBDFayLcDI4ljGokHxmpqaBQuDWa4iAxWaJJ1PS8vAmbntjjKmQ=="
|
|
||||||
},
|
|
||||||
"nanoid@3.3.11": {
|
|
||||||
"integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
|
|
||||||
"bin": true
|
|
||||||
},
|
|
||||||
"node-domexception@1.0.0": {
|
|
||||||
"integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==",
|
|
||||||
"deprecated": true
|
|
||||||
},
|
|
||||||
"node-fetch@3.3.2": {
|
|
||||||
"integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==",
|
|
||||||
"dependencies": [
|
|
||||||
"data-uri-to-buffer",
|
|
||||||
"fetch-blob",
|
|
||||||
"formdata-polyfill"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"obsidian@1.12.2_@codemirror+state@6.5.0_@codemirror+view@6.38.6": {
|
"obsidian@1.12.2_@codemirror+state@6.5.0_@codemirror+view@6.38.6": {
|
||||||
"integrity": "sha512-DGAzpt6vo+sMDET/o8Zj26Bj2hbHrFsNUU8TtnzCyerO/OHksMFQnU9QEnPHVsOMstt/WnHXfC56j2r1syObWg==",
|
"integrity": "sha512-DGAzpt6vo+sMDET/o8Zj26Bj2hbHrFsNUU8TtnzCyerO/OHksMFQnU9QEnPHVsOMstt/WnHXfC56j2r1syObWg==",
|
||||||
"dependencies": [
|
"dependencies": [
|
||||||
@@ -1119,40 +718,6 @@
|
|||||||
"moment"
|
"moment"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"package-json-from-dist@1.0.1": {
|
|
||||||
"integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw=="
|
|
||||||
},
|
|
||||||
"path-scurry@2.0.2": {
|
|
||||||
"integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==",
|
|
||||||
"dependencies": [
|
|
||||||
"lru-cache",
|
|
||||||
"minipass"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"picocolors@1.1.1": {
|
|
||||||
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="
|
|
||||||
},
|
|
||||||
"picomatch@4.0.3": {
|
|
||||||
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="
|
|
||||||
},
|
|
||||||
"postcss@8.5.6": {
|
|
||||||
"integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==",
|
|
||||||
"dependencies": [
|
|
||||||
"nanoid",
|
|
||||||
"picocolors",
|
|
||||||
"source-map-js"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"react-dom@19.1.0_react@19.1.0": {
|
|
||||||
"integrity": "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==",
|
|
||||||
"dependencies": [
|
|
||||||
"react",
|
|
||||||
"scheduler"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"react@19.1.0": {
|
|
||||||
"integrity": "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg=="
|
|
||||||
},
|
|
||||||
"remark-frontmatter@5.0.0": {
|
"remark-frontmatter@5.0.0": {
|
||||||
"integrity": "sha512-XTFYvNASMe5iPN0719nPrdItC9aU0ssC4v14mH1BCi1u0n1gAocqcujWUrByftZTbLhRtiKRyjYTSIOcr69UVQ==",
|
"integrity": "sha512-XTFYvNASMe5iPN0719nPrdItC9aU0ssC4v14mH1BCi1u0n1gAocqcujWUrByftZTbLhRtiKRyjYTSIOcr69UVQ==",
|
||||||
"dependencies": [
|
"dependencies": [
|
||||||
@@ -1190,52 +755,16 @@
|
|||||||
"unified"
|
"unified"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"rimraf@6.1.3": {
|
|
||||||
"integrity": "sha512-LKg+Cr2ZF61fkcaK1UdkH2yEBBKnYjTyWzTJT6KNPcSPaiT7HSdhtMXQuN5wkTX0Xu72KQ1l8S42rlmexS2hSA==",
|
|
||||||
"dependencies": [
|
|
||||||
"glob",
|
|
||||||
"package-json-from-dist"
|
|
||||||
],
|
|
||||||
"bin": true
|
|
||||||
},
|
|
||||||
"scheduler@0.26.0": {
|
|
||||||
"integrity": "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA=="
|
|
||||||
},
|
|
||||||
"source-map-js@1.2.1": {
|
|
||||||
"integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="
|
|
||||||
},
|
|
||||||
"style-mod@4.1.3": {
|
"style-mod@4.1.3": {
|
||||||
"integrity": "sha512-i/n8VsZydrugj3Iuzll8+x/00GH2vnYsk1eomD8QiRrSAeW6ItbCQDtfXCeJHd0iwiNagqjQkvpvREEPtW3IoQ=="
|
"integrity": "sha512-i/n8VsZydrugj3Iuzll8+x/00GH2vnYsk1eomD8QiRrSAeW6ItbCQDtfXCeJHd0iwiNagqjQkvpvREEPtW3IoQ=="
|
||||||
},
|
},
|
||||||
"tailwindcss@4.2.0": {
|
|
||||||
"integrity": "sha512-yYzTZ4++b7fNYxFfpnberEEKu43w44aqDMNM9MHMmcKuCH7lL8jJ4yJ7LGHv7rSwiqM0nkiobF9I6cLlpS2P7Q=="
|
|
||||||
},
|
|
||||||
"tapable@2.3.0": {
|
|
||||||
"integrity": "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg=="
|
|
||||||
},
|
|
||||||
"tar@7.5.9": {
|
|
||||||
"integrity": "sha512-BTLcK0xsDh2+PUe9F6c2TlRp4zOOBMTkoQHQIWSIzI0R7KG46uEwq4OPk2W7bZcprBMsuaeFsqwYr7pjh6CuHg==",
|
|
||||||
"dependencies": [
|
|
||||||
"@isaacs/fs-minipass",
|
|
||||||
"chownr",
|
|
||||||
"minipass",
|
|
||||||
"minizlib",
|
|
||||||
"yallist"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"trough@2.2.0": {
|
"trough@2.2.0": {
|
||||||
"integrity": "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw=="
|
"integrity": "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw=="
|
||||||
},
|
},
|
||||||
"tslib@2.8.1": {
|
|
||||||
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="
|
|
||||||
},
|
|
||||||
"typescript@5.9.3": {
|
"typescript@5.9.3": {
|
||||||
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
||||||
"bin": true
|
"bin": true
|
||||||
},
|
},
|
||||||
"undici-types@6.21.0": {
|
|
||||||
"integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="
|
|
||||||
},
|
|
||||||
"unified@11.0.5": {
|
"unified@11.0.5": {
|
||||||
"integrity": "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==",
|
"integrity": "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==",
|
||||||
"dependencies": [
|
"dependencies": [
|
||||||
@@ -1275,10 +804,6 @@
|
|||||||
"unist-util-visit-parents"
|
"unist-util-visit-parents"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"uuid@11.1.0": {
|
|
||||||
"integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==",
|
|
||||||
"bin": true
|
|
||||||
},
|
|
||||||
"vfile-message@4.0.3": {
|
"vfile-message@4.0.3": {
|
||||||
"integrity": "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==",
|
"integrity": "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==",
|
||||||
"dependencies": [
|
"dependencies": [
|
||||||
@@ -1296,45 +821,25 @@
|
|||||||
"w3c-keyname@2.2.8": {
|
"w3c-keyname@2.2.8": {
|
||||||
"integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ=="
|
"integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ=="
|
||||||
},
|
},
|
||||||
"web-streams-polyfill@3.3.3": {
|
|
||||||
"integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw=="
|
|
||||||
},
|
|
||||||
"yallist@5.0.0": {
|
|
||||||
"integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw=="
|
|
||||||
},
|
|
||||||
"zwitch@2.0.4": {
|
"zwitch@2.0.4": {
|
||||||
"integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A=="
|
"integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A=="
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"workspace": {
|
"workspace": {
|
||||||
"packageJson": {
|
|
||||||
"dependencies": [
|
"dependencies": [
|
||||||
|
"jsr:@deno/esbuild-plugin@^1.2.1",
|
||||||
"npm:@flowershow/remark-wiki-link@^3.4.0",
|
"npm:@flowershow/remark-wiki-link@^3.4.0",
|
||||||
"npm:@instantdb/admin@~0.21.3",
|
|
||||||
"npm:@instantdb/react@~0.20.28",
|
|
||||||
"npm:@react-oauth/google@~0.12.2",
|
|
||||||
"npm:@tailwindcss/postcss@4",
|
|
||||||
"npm:@types/node@20",
|
|
||||||
"npm:@types/react-dom@19",
|
|
||||||
"npm:@types/react@19",
|
|
||||||
"npm:@wasm-tool/rollup-plugin-rust@^3.1.4",
|
|
||||||
"npm:binaryen@125",
|
|
||||||
"npm:builtin-modules@5",
|
"npm:builtin-modules@5",
|
||||||
"npm:esbuild@~0.27.3",
|
"npm:esbuild@~0.27.3",
|
||||||
"npm:mdast@3",
|
"npm:mdast@3",
|
||||||
"npm:obsidian@^1.12.2",
|
"npm:obsidian@^1.12.2",
|
||||||
"npm:react-dom@19.1.0",
|
|
||||||
"npm:react@19.1.0",
|
|
||||||
"npm:remark-frontmatter@5",
|
"npm:remark-frontmatter@5",
|
||||||
"npm:remark-gfm@^4.0.1",
|
"npm:remark-gfm@^4.0.1",
|
||||||
"npm:remark-parse@11",
|
"npm:remark-parse@11",
|
||||||
"npm:remark-stringify@11",
|
"npm:remark-stringify@11",
|
||||||
"npm:tailwindcss@4",
|
|
||||||
"npm:tslib@^2.6.2",
|
|
||||||
"npm:typescript@^5.9.3",
|
"npm:typescript@^5.9.3",
|
||||||
"npm:unified@^11.0.5",
|
"npm:unified@^11.0.5",
|
||||||
"npm:unist-util-visit@^5.1.0"
|
"npm:unist-util-visit@^5.1.0"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
10
apps/obsidian-plugin/manifest.json
Normal file
10
apps/obsidian-plugin/manifest.json
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"id": "mize",
|
||||||
|
"name": "Mize",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"minAppVersion": "1.5.0",
|
||||||
|
"main": "main.js",
|
||||||
|
"author": "c2vi",
|
||||||
|
"isDesktopOnly": true,
|
||||||
|
"description": "Turns Obsidian into a full Mize Explorer"
|
||||||
|
}
|
||||||
61
apps/obsidian-plugin/mod.rs
Normal file
61
apps/obsidian-plugin/mod.rs
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
use dioxus::prelude::*;
|
||||||
|
use marts::ClientAuth;
|
||||||
|
use mize::Mize;
|
||||||
|
use wasm_bindgen::prelude::*;
|
||||||
|
use serde::{Serialize, Deserialize};
|
||||||
|
use mize::{MizeResult, mize_part, MizePart, MizePartCreate, error::IntoMizeResultJsValue};
|
||||||
|
use mize::platform::js::JsMize;
|
||||||
|
|
||||||
|
|
||||||
|
// console_log macro
|
||||||
|
#[wasm_bindgen]
|
||||||
|
extern "C" {
|
||||||
|
#[wasm_bindgen(js_namespace = console)]
|
||||||
|
fn log(s: &str);
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! console_log {
|
||||||
|
// Note that this is using the `log` function imported above during
|
||||||
|
// `bare_bones`
|
||||||
|
($($t:tt)*) => ({ log(&format_args!($($t)*).to_string())})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
|
pub fn test_fn(mize: &mut JsMize) {
|
||||||
|
let mize = mize.inner();
|
||||||
|
console_log!("test fn...");
|
||||||
|
|
||||||
|
if let Err(e) = try_test_fn(mize) {
|
||||||
|
console_log!("err: {:?}", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fn try_test_fn(mize: &mut Mize) -> MizeResult<()> {
|
||||||
|
//let auth = mize.get_part_native::<ClientAuth>("client_auth")?;
|
||||||
|
//console_log!("secrets: {:?}", auth.secrets());
|
||||||
|
|
||||||
|
let parts = mize.parts.lock().unwrap();
|
||||||
|
for part in parts.keys() {
|
||||||
|
console_log!("part: {}", part)
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
|
pub fn part_obsidian_init(mize: &mut JsMize, plugin: JsValue, app: JsValue) {
|
||||||
|
let mut mize = mize.inner();
|
||||||
|
if let Err(e) = obsidian_mize_main(&mut mize, plugin, app) {
|
||||||
|
console_log!("Failed to start the obsidian mize part: {:?}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn obsidian_mize_main(mize: &mut Mize, plugin: JsValue, app: JsValue) -> MizeResult<()> {
|
||||||
|
|
||||||
|
marts::part_obsidian(mize, plugin, app)?;
|
||||||
|
marts::part_client_auth(mize)?;
|
||||||
|
marts::spacetime(mize)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
173
apps/obsidian-plugin/plugin.ts
Normal file
173
apps/obsidian-plugin/plugin.ts
Normal file
@@ -0,0 +1,173 @@
|
|||||||
|
import { Plugin, ItemView, TFile, Modal } from "obsidian";
|
||||||
|
import wasmBinary from "./rust_dist/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();
|
||||||
|
}
|
||||||
|
}
|
||||||
3
apps/obsidian-plugin/src/main.rs
Normal file
3
apps/obsidian-plugin/src/main.rs
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
fn main() {
|
||||||
|
println!("Hello, world!");
|
||||||
|
}
|
||||||
13
apps/os/Cargo.toml
Normal file
13
apps/os/Cargo.toml
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
[package]
|
||||||
|
name = "mize-bin"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
mize = { workspace = true }
|
||||||
|
clap = { version = "4.5.59", features = ["string"] }
|
||||||
|
tracing = "0.1.44"
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "mize"
|
||||||
|
path = "./src/main.rs"
|
||||||
161
apps/os/src/cli.rs
Normal file
161
apps/os/src/cli.rs
Normal file
@@ -0,0 +1,161 @@
|
|||||||
|
use clap::ArgMatches;
|
||||||
|
use core::result::Result::Err;
|
||||||
|
use core::unimplemented;
|
||||||
|
use mize::flume::bounded;
|
||||||
|
use mize::{instance, mize_err};
|
||||||
|
use std::ffi::OsString;
|
||||||
|
use std::io::Read;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
use std::str::FromStr;
|
||||||
|
use tracing::debug;
|
||||||
|
use mize::ciborium;
|
||||||
|
|
||||||
|
use mize::error::{IntoMizeResult, MizeError, MizeResult, MizeResultTrait};
|
||||||
|
use mize::instance::subscription::Subscription;
|
||||||
|
use mize::instance::subscription::Update;
|
||||||
|
use mize::instance::Mize;
|
||||||
|
use mize::item::{IntoItemData, ItemData};
|
||||||
|
use mize::platform::os::config_from_cli_args;
|
||||||
|
use mize::platform::os::fsstore::FileStore;
|
||||||
|
|
||||||
|
use crate::generated;
|
||||||
|
|
||||||
|
pub fn get(sub_matches: &ArgMatches, instance: &mut Mize) -> MizeResult<()> {
|
||||||
|
|
||||||
|
let id = sub_matches
|
||||||
|
.get_one::<String>("id")
|
||||||
|
.ok_or(MizeError::new().msg("No id Argument specified"))?;
|
||||||
|
|
||||||
|
let raw = sub_matches.get_flag("raw");
|
||||||
|
let data = instance.get_config(id)?;
|
||||||
|
|
||||||
|
if raw {
|
||||||
|
print!("{}", data.value_string()?);
|
||||||
|
} else {
|
||||||
|
println!("{}", data);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn call(sub_matches: &ArgMatches, instance: &mut Mize) -> MizeResult<()> {
|
||||||
|
println!("call cmd not implemented");
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create(sub_matches: &ArgMatches, instance: &mut Mize) -> MizeResult<()> {
|
||||||
|
|
||||||
|
let item = instance.new_item()?;
|
||||||
|
|
||||||
|
println!("id: {}", item.id());
|
||||||
|
println!("with namespace: {}", item.id().namespace().as_real_string());
|
||||||
|
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn status(sub_matches: &ArgMatches, instance: &mut Mize) -> MizeResult<()> {
|
||||||
|
println!("not implemented");
|
||||||
|
Err(mize_err!("not implemented"))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn info(sub_matches: &ArgMatches, instance: &mut Mize) -> MizeResult<()> {
|
||||||
|
println!("not implemented");
|
||||||
|
Err(mize_err!("not implemented"))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn run(sub_matches: &ArgMatches, mize: &mut Mize) -> MizeResult<()> {
|
||||||
|
match sub_matches.subcommand() {
|
||||||
|
None => { // run a mize instance (does whatever it's config will say)
|
||||||
|
mize.wait();
|
||||||
|
}
|
||||||
|
Some((runnable, sub_matches)) => { // run smth from a part. for now registered cli commands
|
||||||
|
let mut ext_args: Vec<String> = sub_matches
|
||||||
|
.get_many::<OsString>("")
|
||||||
|
.unwrap_or_default()
|
||||||
|
.map(|v| v.to_string_lossy().to_string())
|
||||||
|
.collect();
|
||||||
|
let mut ext_cmdline = vec![runnable.to_owned()];
|
||||||
|
ext_cmdline.append(&mut ext_args);
|
||||||
|
|
||||||
|
let mut cli = generated::CliPart::get("cli")?;
|
||||||
|
cli.run(ext_cmdline)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set(sub_matches: &ArgMatches, instance: &mut Mize) -> MizeResult<()> {
|
||||||
|
let id = sub_matches
|
||||||
|
.get_one::<String>("id")
|
||||||
|
.ok_or(MizeError::new().msg("No id Argument specified"))?;
|
||||||
|
|
||||||
|
let value = sub_matches
|
||||||
|
.get_one::<String>("value")
|
||||||
|
.ok_or(MizeError::new().msg("No value Argument specified"))?;
|
||||||
|
|
||||||
|
instance.set_blocking(id, value.into_item_data())?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn show(sub_matches: &ArgMatches, instance: &mut Mize) -> MizeResult<()> {
|
||||||
|
println!("not implemented");
|
||||||
|
// let instance = Mize::with_config(config_from_cli_args(sub_matches)?)?;
|
||||||
|
|
||||||
|
// let id = sub_matches
|
||||||
|
// .get_one::<String>("id")
|
||||||
|
// .ok_or(MizeError::new().msg("No id Argument specified"))?;
|
||||||
|
|
||||||
|
// let item = instance.get(id)?;
|
||||||
|
|
||||||
|
// let (tx, rx) = bounded::<Update>(4);
|
||||||
|
// let sub = Subscription::from_sender(tx);
|
||||||
|
// instance.sub(id, sub)?;
|
||||||
|
|
||||||
|
// println!("item: {}", item.as_data_full()?);
|
||||||
|
|
||||||
|
// for update in rx {
|
||||||
|
// println!("####### GOT UPDATE #######");
|
||||||
|
// println!("item: {}", update.new_item()?.as_data_full()?);
|
||||||
|
// }
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn format_cbor(sub_matches: &ArgMatches, instance: &mut Mize) -> MizeResult<()> {
|
||||||
|
let mut input: Vec<u8> = Vec::new();
|
||||||
|
let stdin = std::io::stdin();
|
||||||
|
let mut handle = stdin.lock();
|
||||||
|
//handle.read_to_end(&mut input);
|
||||||
|
|
||||||
|
let cbor_data = ciborium::from_reader(handle)?;
|
||||||
|
let item_data = ItemData::from_cbor(cbor_data);
|
||||||
|
|
||||||
|
println!("{}", item_data);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn run_from_part(cmd: &str, sub_matches: &ArgMatches, instance: &mut Mize) -> MizeResult<()> {
|
||||||
|
let default = OsString::default();
|
||||||
|
let mut sub_cmd_line: Vec<OsString> = sub_matches
|
||||||
|
.get_many::<OsString>("")
|
||||||
|
.unwrap()
|
||||||
|
.map(|e| e.to_owned())
|
||||||
|
.collect();
|
||||||
|
sub_cmd_line.insert(0, OsString::from_str(cmd)?);
|
||||||
|
|
||||||
|
debug!("loading module '{}'", cmd);
|
||||||
|
instance.load_part(cmd)?;
|
||||||
|
|
||||||
|
println!("running cli from part not implemented");
|
||||||
|
//instance
|
||||||
|
//.get_part(cmd)?
|
||||||
|
//.run_cli(&instance, sub_cmd_line)
|
||||||
|
//.ok_or(mize_err!(
|
||||||
|
//"The Part '{cmd}' does not implement the run_cli() functionality"
|
||||||
|
//))??;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
70
apps/os/src/generated.rs
Normal file
70
apps/os/src/generated.rs
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
use mize::prelude::*;
|
||||||
|
|
||||||
|
|
||||||
|
pub struct CliPart {
|
||||||
|
id: MizeId
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MizePart for CliPart {
|
||||||
|
fn id(&self) -> MizeResult<MizeId> {
|
||||||
|
Ok(self.id.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CliPart {
|
||||||
|
pub fn get(name: &str) -> MizeResult<CliPart> {
|
||||||
|
let mut mize = get_global_mize()?;
|
||||||
|
let part = mize.get_part(name)?;
|
||||||
|
Ok(CliPart { id: part.id()? })
|
||||||
|
}
|
||||||
|
pub fn run(&self, cmdline: Vec<String>) -> MizeResult<()> {
|
||||||
|
let cmdline = cmdline.into_item_data();
|
||||||
|
let mut args = ItemData::new();
|
||||||
|
args.set_path("0", cmdline)?;
|
||||||
|
dyn_call(self.id.clone(), "run", args)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
pub fn set_command(&self, name: &str) -> MizeResult<()> {
|
||||||
|
let name = name.into_item_data();
|
||||||
|
let mut args = ItemData::new();
|
||||||
|
args.set_path("0", name)?;
|
||||||
|
dyn_call(self.id.clone(), "set_command", args)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
pub fn subcommand<T: Fn(ArgMatches) -> MizeResult<()> + Send + Sync + 'static>(&self, subcmd: Command, action: T) -> MizeResult<()> {
|
||||||
|
let mut subcmd_arg = ItemData::new();
|
||||||
|
subcmd_arg.set_path("_native", subcmd.native_id)?;
|
||||||
|
let mut action_arg = ItemData::from_native(action)?;
|
||||||
|
let mut args = ItemData::new();
|
||||||
|
args.set_path("0", subcmd_arg)?;
|
||||||
|
args.set_path("1", action_arg)?;
|
||||||
|
dyn_call(self.id.clone(), "subcommand", args)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// the clap::Command native stuff
|
||||||
|
pub struct Command {
|
||||||
|
native_id: u32
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ArgMatches {
|
||||||
|
native_id: u32
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Action {
|
||||||
|
native_id: u32
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Command {
|
||||||
|
pub fn new(name: &str) -> MizeResult<Command> {
|
||||||
|
let name = name.into_item_data();
|
||||||
|
let mut args = ItemData::new();
|
||||||
|
args.set_path("0", name)?;
|
||||||
|
let mut mize = get_global_mize()?;
|
||||||
|
let id = mize.id_new_dots("cli.Command")?;
|
||||||
|
let data = dyn_call(id.clone(), "new", args)?;
|
||||||
|
let native_id: u32 = data.get_path("_native")?.native_take()?;
|
||||||
|
Ok(Command { native_id })
|
||||||
|
}
|
||||||
|
}
|
||||||
177
apps/os/src/main.rs
Normal file
177
apps/os/src/main.rs
Normal file
@@ -0,0 +1,177 @@
|
|||||||
|
#![allow(warnings)]
|
||||||
|
|
||||||
|
use clap::builder::OsStr;
|
||||||
|
use clap::{crate_version, Arg, Command};
|
||||||
|
use clap::{ArgAction, ArgMatches};
|
||||||
|
use std::ffi::OsString;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
use std::process::exit;
|
||||||
|
|
||||||
|
use mize::error::{MizeError, MizeResult};
|
||||||
|
use mize::item::ItemData;
|
||||||
|
use mize::platform::os::config_from_cli_args;
|
||||||
|
use mize::platform::os::logging::init_logger;
|
||||||
|
use mize::{Mize, instance, mize_err};
|
||||||
|
use std::env;
|
||||||
|
use std::io::Write;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use tracing::{debug, error, info, trace, warn};
|
||||||
|
|
||||||
|
mod cli;
|
||||||
|
// the generated module by hand. as we need the mize-bin to run generate
|
||||||
|
mod generated;
|
||||||
|
|
||||||
|
static APPNAME: &str = "mize";
|
||||||
|
|
||||||
|
macro_rules! compile_in_parts {
|
||||||
|
(
|
||||||
|
$mize:ident,
|
||||||
|
$(
|
||||||
|
$name:literal : $part:path
|
||||||
|
),+ $(,)?
|
||||||
|
) => {
|
||||||
|
let enable = $mize.get_config("enable")?;
|
||||||
|
let mut enable_str = Vec::new();
|
||||||
|
for name in enable.cbor().as_array().ok_or(mize_err!("config(enable) not a list"))? {
|
||||||
|
enable_str.push(name.as_text().ok_or("part name not a str")?);
|
||||||
|
}
|
||||||
|
$(
|
||||||
|
if enable_str.contains(&$name) {
|
||||||
|
tracing::info!("enabling compiled_in_part: {}", $name);
|
||||||
|
if let Err(e) = $part(&mut $mize) {
|
||||||
|
print!("part '{}' failed with: ", $name);
|
||||||
|
e.log();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)+
|
||||||
|
};
|
||||||
|
}
|
||||||
|
fn main() {
|
||||||
|
if let Err(e) = tried_main() {
|
||||||
|
e.log();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn tried_main() -> MizeResult<()> {
|
||||||
|
let cli_matches = cli_matches();
|
||||||
|
|
||||||
|
// logging
|
||||||
|
init_logger(Some(&cli_matches));
|
||||||
|
|
||||||
|
// init mize
|
||||||
|
let mut mize = Mize::with_config(config_from_cli_args(&cli_matches)?)?;
|
||||||
|
|
||||||
|
// compiled in parts (which for now is all)
|
||||||
|
/*
|
||||||
|
compile_in_parts! {
|
||||||
|
mize,
|
||||||
|
"cli": marts::cli,
|
||||||
|
"auth": marts::part_client_auth,
|
||||||
|
"spacetime": marts::spacetime,
|
||||||
|
//"matrix": marts::part_matrix,
|
||||||
|
"ppc.server": marts::ppc::server::part_ppc_server,
|
||||||
|
"c2vi": marts::part_c2vi,
|
||||||
|
"sci.timetrack": marts::sci::timetrack::part_timetrack,
|
||||||
|
};
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
// match subcommand
|
||||||
|
match cli_matches.subcommand() {
|
||||||
|
Some(("run", sub_matches)) => cli::run(sub_matches, &mut mize),
|
||||||
|
Some(("status", sub_matches)) => cli::status(sub_matches, &mut mize),
|
||||||
|
Some(("info", sub_matches)) => cli::info(sub_matches, &mut mize),
|
||||||
|
|
||||||
|
Some(("get", sub_matches)) => cli::get(sub_matches, &mut mize),
|
||||||
|
Some(("set", sub_matches)) => cli::set(sub_matches, &mut mize),
|
||||||
|
Some(("show", sub_matches)) => cli::show(sub_matches, &mut mize),
|
||||||
|
Some(("call", sub_matches)) => cli::call(sub_matches, &mut mize),
|
||||||
|
Some(("create", sub_matches)) => cli::create(sub_matches, &mut mize),
|
||||||
|
Some(("format-cbor", sub_matches)) => cli::format_cbor(sub_matches, &mut mize),
|
||||||
|
|
||||||
|
Some((cmd, sub_matches)) => cli::run_from_part(cmd, sub_matches, &mut mize),
|
||||||
|
None => Err(MizeError::new()
|
||||||
|
.msg("No subcommand was passed. use --help to list availavle comamnds.")),
|
||||||
|
}?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cli_matches() -> clap::ArgMatches {
|
||||||
|
let main = Command::new(APPNAME)
|
||||||
|
.version(crate_version!())
|
||||||
|
.author("Sebastian Moser")
|
||||||
|
.about("MiZe")
|
||||||
|
.allow_external_subcommands(true)
|
||||||
|
.arg(
|
||||||
|
Arg::new("verbose")
|
||||||
|
.long("verbose")
|
||||||
|
.short('v')
|
||||||
|
.action(ArgAction::Count)
|
||||||
|
.global(true),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::new("log-level")
|
||||||
|
.long("log-level")
|
||||||
|
.value_name("LOGLEVEL")
|
||||||
|
.help("set the log-level to one of OFF, ERROR, WARN, INFO, DEBUG, TRACE")
|
||||||
|
.global(true),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::new("log")
|
||||||
|
.long("log")
|
||||||
|
.help("log file to use")
|
||||||
|
.global(true),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::new("config")
|
||||||
|
.long("config")
|
||||||
|
.help("config file path")
|
||||||
|
.global(true),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::new("secrets")
|
||||||
|
.long("secrets")
|
||||||
|
.help("file path to store secrets at")
|
||||||
|
.global(true),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::new("opt")
|
||||||
|
.short('o')
|
||||||
|
.long("opt")
|
||||||
|
.help("overwrite a config option")
|
||||||
|
.global(true),
|
||||||
|
)
|
||||||
|
.subcommand(
|
||||||
|
Command::new("get")
|
||||||
|
.aliases(["g"])
|
||||||
|
.arg(Arg::new("id").help("The id or path to get"))
|
||||||
|
.arg(Arg::new("recurse").short('r').action(ArgAction::Count))
|
||||||
|
.arg(
|
||||||
|
Arg::new("raw")
|
||||||
|
.long("raw")
|
||||||
|
.action(ArgAction::SetTrue)
|
||||||
|
.help("print the config opt value as a raw string"),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.subcommand(
|
||||||
|
Command::new("set")
|
||||||
|
.aliases(["s"])
|
||||||
|
.arg(Arg::new("id").help("The id or path to set"))
|
||||||
|
.arg(Arg::new("value").help("The value to set the path to")),
|
||||||
|
)
|
||||||
|
.subcommand(
|
||||||
|
Command::new("show")
|
||||||
|
.aliases(["so"])
|
||||||
|
.arg(Arg::new("id").help("The id or path to sub to and show")),
|
||||||
|
)
|
||||||
|
.subcommand(Command::new("call").aliases(["c"]))
|
||||||
|
.subcommand(Command::new("create").aliases(["cr"]))
|
||||||
|
.subcommand(Command::new("status").aliases(["st"]))
|
||||||
|
.subcommand(Command::new("run").allow_external_subcommands(true))
|
||||||
|
.subcommand(Command::new("format-cbor").aliases(["fc"]))
|
||||||
|
.arg_required_else_help(true);
|
||||||
|
|
||||||
|
return main.get_matches();
|
||||||
|
}
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
# Source code architecture
|
|
||||||
|
|
||||||
## 1. Core Mize Framework (`packages/mize/`)
|
|
||||||
|
|
||||||
**Architecture layers:**
|
|
||||||
- `core/`: Platform-independent logic
|
|
||||||
- `config/`: Configuration management
|
|
||||||
- `error/`: Error handling
|
|
||||||
- `id/`: Unique identifier system
|
|
||||||
- `instance/`: Mize runtime instances
|
|
||||||
- `item/`: Filesystem item abstractions
|
|
||||||
- `memstore/`: In-memory storage
|
|
||||||
- `proto/`: Protocol definitions
|
|
||||||
- `types/`: Core type definitions
|
|
||||||
|
|
||||||
- `platform/`: Platform-specific implementations
|
|
||||||
- `os/`: Native OS integration
|
|
||||||
- `wasm/`: WebAssembly support
|
|
||||||
- `bare/`: Bare metal targets
|
|
||||||
- `jvm/`: Java Virtual Machine integration
|
|
||||||
|
|
||||||
### 2. Marts (`packages/marts/`)
|
|
||||||
- **Language**: Rust + TypeScript
|
|
||||||
- **Purpose**: Collection of Mize framework parts and utilities
|
|
||||||
- **Key features**:
|
|
||||||
- CLI tools
|
|
||||||
- JavaScript/TypeScript integration
|
|
||||||
- Habitica integration
|
|
||||||
- Deno-based scripting support
|
|
||||||
|
|
||||||
## 3. MME - Mize Module Environment (`packages/mme/`)
|
|
||||||
- **Language**: Rust
|
|
||||||
- **Purpose**: Module execution environment
|
|
||||||
- **Key features**:
|
|
||||||
- Cross-platform module loading
|
|
||||||
- Qt and Slint UI support
|
|
||||||
- Web view integration (tao/wry)
|
|
||||||
- Command system (comandr)
|
|
||||||
|
|
||||||
## 4. PPC - Platform Components (`packages/ppc/`)
|
|
||||||
- **Language**: Rust + TypeScript
|
|
||||||
- **Purpose**: Platform-specific implementations
|
|
||||||
- **Targets**: Obsidian plugin, OS integration
|
|
||||||
|
|
||||||
## 5. Vic - Victor CLI (`packages/vic/`)
|
|
||||||
- **Language**: Rust
|
|
||||||
- **Purpose**: Command-line interface tool
|
|
||||||
- **Features**: Build, run, test, and manage Mize applications
|
|
||||||
@@ -1,30 +1,22 @@
|
|||||||
|
|
||||||
[package]
|
[package]
|
||||||
name = "mize"
|
name = "mize"
|
||||||
version = "0.0.1"
|
version = "0.0.1"
|
||||||
edition = "2021"
|
edition = "2024"
|
||||||
license = "GPL-3.0"
|
license = "GPL-3.0"
|
||||||
description = 'A strongly typed "filesystem" for the age of connectivity.'
|
description = 'A strongly typed "filesystem" for the age of connectivity.'
|
||||||
repository = "https://github.com/c2vi/mize"
|
repository = "https://git.ppc.social/ppc/mize"
|
||||||
|
|
||||||
[package.metadata.wasm-pack.profile.dev]
|
|
||||||
wasm-opt = false
|
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
name = "mize"
|
name = "mize"
|
||||||
path = "src/lib.rs"
|
path = "src/lib.rs"
|
||||||
crate-type = ["cdylib", "rlib"]
|
crate-type = [ "cdylib", "rlib" ]
|
||||||
|
|
||||||
[[bin]]
|
|
||||||
name = "mize"
|
|
||||||
path = "src/platform/os/main.rs"
|
|
||||||
required-features = [ "target-os" ]
|
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = [ "target-os" ]
|
default = [ "target-os" ]
|
||||||
target-os= [ "clap", "home", "nix", "sysinfo", "ciborium/default", "tracing-subscriber", "async", "tracing-subscriber/env-filter", "libloading", "ciborium-io", "tracing-core", "tar", "flate2", "http_req" ]
|
target-os = [ "clap", "home", "nix", "sysinfo", "ciborium/default", "tracing-subscriber", "async", "tracing-subscriber/env-filter", "libloading", "ciborium-io", "tracing-core", "tar", "flate2", "http_req", "tokio/net", "tokio", "tokio/rt-multi-thread", "tokio/io-util", "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"]
|
target-js = [ "wasm-bindgen", "console_error_panic_hook", "web-sys", "web-sys/Worker", "web-sys/Window", "web-sys/Request", "web-sys/RequestInit", "web-sys/RequestMode", "web-sys/Response", "web-sys/WorkerOptions", "web-sys/WorkerType", "serde-wasm-bindgen", "wasm-bindgen-futures", "wee_alloc", "ciborium/default" ]
|
||||||
async = ["tokio/net", "tokio", "tokio/rt-multi-thread", "tokio/io-util"]
|
async = [ "tokio", ]
|
||||||
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
# core dependencies
|
# core dependencies
|
||||||
@@ -38,13 +30,10 @@ serde_json = { version = "1.0.117", optional = false }
|
|||||||
toml = { version = "0.8.13", features = ["parse"], optional = false }
|
toml = { version = "0.8.13", features = ["parse"], optional = false }
|
||||||
dyn-clone = "1.0.17"
|
dyn-clone = "1.0.17"
|
||||||
sha2 = "0.10.8"
|
sha2 = "0.10.8"
|
||||||
flume = "0.11.1"
|
flume = { version = "0.11.1", default-features = false, features = [ "async" ] }
|
||||||
mize_macros = { path = "../ac_mize_macros" }
|
mize_macros = { path = "../macros" }
|
||||||
async-trait = "0.1.89"
|
async-trait = "0.1.89"
|
||||||
|
|
||||||
# builtin modules
|
|
||||||
#mize_module_blob = { path = "./modules/modules/Blob" }
|
|
||||||
|
|
||||||
# wasm dependencies
|
# wasm dependencies
|
||||||
wasm-bindgen = { version = "0.2.84", optional = true }
|
wasm-bindgen = { version = "0.2.84", optional = true }
|
||||||
console_error_panic_hook = { version = "0.1.7", optional = true }
|
console_error_panic_hook = { version = "0.1.7", optional = true }
|
||||||
@@ -60,38 +49,11 @@ daemonize = { version = "0.5.0", optional = true }
|
|||||||
nix = { version = "0.27.1", features = ["signal"], optional = true }
|
nix = { version = "0.27.1", features = ["signal"], optional = true }
|
||||||
sysinfo = { version = "0.30.12", optional = true }
|
sysinfo = { version = "0.30.12", optional = true }
|
||||||
tracing-subscriber = { version = "0.3.18", optional = true }
|
tracing-subscriber = { version = "0.3.18", optional = true }
|
||||||
tokio = { version = "1.46.1", features = ["sync"], optional = true }
|
tokio = { version = "1.46.1", features = ["sync", "rt" ], optional = true, default-features = false }
|
||||||
libloading = { version = "0.8.5", optional = true }
|
libloading = { version = "0.8.5", optional = true }
|
||||||
ciborium-io = { version = "0.2.2", optional = true }
|
ciborium-io = { version = "0.2.2", optional = true }
|
||||||
tracing-core = { version = "0.1.32", optional = true }
|
tracing-core = { version = "0.1.32", optional = true }
|
||||||
tar = { version = "0.4.42", optional = true }
|
tar = { version = "0.4.42", optional = true }
|
||||||
flate2 = { version = "1.0.33", 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 = { 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" ] }
|
cmd_lib = { version = "2.0.1", optional = true }
|
||||||
|
|
||||||
# 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"
|
|
||||||
11
core/src/api.rs
Normal file
11
core/src/api.rs
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
use crate::prelude::*;
|
||||||
|
|
||||||
|
pub fn dyn_call(part_id: MizeId, method: &str, args: ItemData) -> MizeResult<ItemData> {
|
||||||
|
let mut mize = get_global_mize()?;
|
||||||
|
mize.dyn_call(part_id, method, args)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_config(name: &str) -> MizeResult<ItemData> {
|
||||||
|
let mut mize = get_global_mize()?;
|
||||||
|
mize.get_config(name)
|
||||||
|
}
|
||||||
255
core/src/c_api.rs
Normal file
255
core/src/c_api.rs
Normal file
@@ -0,0 +1,255 @@
|
|||||||
|
//! C ABI for dynamically loaded parts (see `include/mize.h`).
|
||||||
|
//!
|
||||||
|
//! Names passed to `opt` / `method` / `thread` / `get_config` / `set_config`
|
||||||
|
//! are absolute dotted paths (e.g. `"vicinae.open"`).
|
||||||
|
//!
|
||||||
|
//! Errors use a thread-local slot (like errno): C code calls `set_err(...)`;
|
||||||
|
//! Rust checks via [`take_err`] after the call.
|
||||||
|
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use std::ffi::{c_char, c_void, CStr};
|
||||||
|
use std::ptr;
|
||||||
|
|
||||||
|
use crate::config::{ConfigOpt, MizeFun};
|
||||||
|
use crate::error::MizeError;
|
||||||
|
use crate::get_global_mize;
|
||||||
|
use crate::item::ItemData;
|
||||||
|
use crate::mize_err;
|
||||||
|
use crate::write_to_mize_stdout;
|
||||||
|
|
||||||
|
type CFun = unsafe extern "C" fn(*mut ItemData) -> *mut ItemData;
|
||||||
|
|
||||||
|
thread_local! {
|
||||||
|
/// Per-thread error set by C via `set_err`, consumed by Rust after the call.
|
||||||
|
static CUR_ERR: RefCell<Option<MizeError>> = const { RefCell::new(None) };
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Clear the thread-local C error (call before invoking a C part entry / callback).
|
||||||
|
pub fn clear_err() {
|
||||||
|
CUR_ERR.with(|slot| {
|
||||||
|
*slot.borrow_mut() = None;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Take the thread-local C error, if any.
|
||||||
|
pub fn take_err() -> Option<MizeError> {
|
||||||
|
CUR_ERR.with(|slot| slot.borrow_mut().take())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Opaque pointer stored via `data_from_native` / read via `data_as_native`.
|
||||||
|
struct NativePtr(*mut c_void);
|
||||||
|
|
||||||
|
// SAFETY: the C++ part is responsible for only using the pointer on the right thread.
|
||||||
|
unsafe impl Send for NativePtr {}
|
||||||
|
unsafe impl Sync for NativePtr {}
|
||||||
|
|
||||||
|
fn cstr<'a>(p: *const c_char) -> Option<&'a str> {
|
||||||
|
if p.is_null() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
unsafe { CStr::from_ptr(p) }.to_str().ok()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn wrap_c_fun(c_fun: CFun) -> MizeFun {
|
||||||
|
Box::new(move |args| {
|
||||||
|
clear_err();
|
||||||
|
let mut args_box = Box::new(args);
|
||||||
|
let ret_ptr = unsafe { c_fun(args_box.as_mut()) };
|
||||||
|
drop(args_box);
|
||||||
|
if let Some(err) = take_err() {
|
||||||
|
if !ret_ptr.is_null() {
|
||||||
|
unsafe { drop(Box::from_raw(ret_ptr)) };
|
||||||
|
}
|
||||||
|
return Err(err);
|
||||||
|
}
|
||||||
|
if ret_ptr.is_null() {
|
||||||
|
return Ok(ItemData::new());
|
||||||
|
}
|
||||||
|
Ok(*unsafe { Box::from_raw(ret_ptr) })
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn register_fun(name: &str, kind: &str, signature: &str, fun: MizeFun) {
|
||||||
|
let mut mize = match get_global_mize() {
|
||||||
|
Ok(m) => m,
|
||||||
|
Err(e) => {
|
||||||
|
let _ = write_to_mize_stdout(format_args!("[c_api] get_global_mize failed: {e:?}\n"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let id = match mize.id_new_dots(name) {
|
||||||
|
Ok(id) => id,
|
||||||
|
Err(e) => {
|
||||||
|
let _ = write_to_mize_stdout(format_args!("[c_api] bad id '{name}': {e:?}\n"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if mize.is_generate_mode().unwrap_or(false) {
|
||||||
|
let short = name.rsplit('.').next().unwrap_or(name);
|
||||||
|
let _ = mize.record_method(short, id.clone(), kind, signature);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let opt = ConfigOpt {
|
||||||
|
name: id.dot(),
|
||||||
|
val: None,
|
||||||
|
fun: None,
|
||||||
|
deps: Vec::new(),
|
||||||
|
};
|
||||||
|
mize.config_opts.lock().unwrap().insert(id.clone(), opt);
|
||||||
|
mize.fun_values.lock().unwrap().insert(id, fun);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub extern "C" fn opt(name: *const c_char) {
|
||||||
|
let Some(name) = cstr(name) else { return };
|
||||||
|
let mut mize = match get_global_mize() {
|
||||||
|
Ok(m) => m,
|
||||||
|
Err(_) => return,
|
||||||
|
};
|
||||||
|
let Ok(id) = mize.id_new_dots(name) else { return };
|
||||||
|
|
||||||
|
if mize.is_generate_mode().unwrap_or(false) {
|
||||||
|
let short = name.rsplit('.').next().unwrap_or(name);
|
||||||
|
let _ = mize.record_opt(short, id.clone(), None);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let opt = ConfigOpt {
|
||||||
|
name: id.dot(),
|
||||||
|
val: None,
|
||||||
|
fun: None,
|
||||||
|
deps: Vec::new(),
|
||||||
|
};
|
||||||
|
mize.config_opts.lock().unwrap().insert(id, opt);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub extern "C" fn method(name: *const c_char, signature: *const c_char, c_fun: CFun) {
|
||||||
|
let Some(name) = cstr(name) else { return };
|
||||||
|
let sig = cstr(signature).unwrap_or("");
|
||||||
|
register_fun(name, "method", sig, wrap_c_fun(c_fun));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub extern "C" fn effect(name: *const c_char, c_fun: CFun) {
|
||||||
|
let Some(name) = cstr(name) else { return };
|
||||||
|
register_fun(name, "effect", "", wrap_c_fun(c_fun));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub extern "C" fn apply(name: *const c_char, c_fun: CFun) {
|
||||||
|
let Some(name) = cstr(name) else { return };
|
||||||
|
register_fun(name, "apply", "", wrap_c_fun(c_fun));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub extern "C" fn thread(name: *const c_char, c_fun: CFun) {
|
||||||
|
let Some(name) = cstr(name) else { return };
|
||||||
|
register_fun(name, "thread", "", wrap_c_fun(c_fun));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub extern "C" fn set_config(name: *const c_char, value: *mut ItemData) {
|
||||||
|
if value.is_null() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let Some(name) = cstr(name) else {
|
||||||
|
unsafe { drop(Box::from_raw(value)) };
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let value = unsafe { *Box::from_raw(value) };
|
||||||
|
let mut mize = match get_global_mize() {
|
||||||
|
Ok(m) => m,
|
||||||
|
Err(_) => return,
|
||||||
|
};
|
||||||
|
let Ok(id) = mize.id_new_dots(name) else { return };
|
||||||
|
|
||||||
|
let mut opts = mize.config_opts.lock().unwrap();
|
||||||
|
match opts.get_mut(&id) {
|
||||||
|
Some(opt) => {
|
||||||
|
opt.val = Some(value);
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
opts.insert(
|
||||||
|
id.clone(),
|
||||||
|
ConfigOpt {
|
||||||
|
name: id.dot(),
|
||||||
|
val: Some(value),
|
||||||
|
fun: None,
|
||||||
|
deps: Vec::new(),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub extern "C" fn get_config(name: *const c_char) -> *mut ItemData {
|
||||||
|
let Some(name) = cstr(name) else {
|
||||||
|
return ptr::null_mut();
|
||||||
|
};
|
||||||
|
let mut mize = match get_global_mize() {
|
||||||
|
Ok(m) => m,
|
||||||
|
Err(_) => return ptr::null_mut(),
|
||||||
|
};
|
||||||
|
match mize.get_config(name) {
|
||||||
|
Ok(data) => Box::into_raw(Box::new(data)),
|
||||||
|
Err(_) => ptr::null_mut(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub extern "C" fn data_new() -> *mut ItemData {
|
||||||
|
Box::into_raw(Box::new(ItemData::new()))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub extern "C" fn data_from_native(ptr: *mut c_void) -> *mut ItemData {
|
||||||
|
match ItemData::from_native(NativePtr(ptr)) {
|
||||||
|
Ok(data) => Box::into_raw(Box::new(data)),
|
||||||
|
Err(e) => {
|
||||||
|
let _ = write_to_mize_stdout(format_args!("[c_api] data_from_native: {e:?}\n"));
|
||||||
|
ptr::null_mut()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub extern "C" fn data_as_native(data: *mut ItemData) -> *mut c_void {
|
||||||
|
if data.is_null() {
|
||||||
|
return ptr::null_mut();
|
||||||
|
}
|
||||||
|
let data = unsafe { &*data };
|
||||||
|
match data.native::<NativePtr>() {
|
||||||
|
Ok(guard) => guard.0,
|
||||||
|
Err(e) => {
|
||||||
|
let _ = write_to_mize_stdout(format_args!("[c_api] data_as_native: {e:?}\n"));
|
||||||
|
ptr::null_mut()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub extern "C" fn data_free(data: *mut ItemData) {
|
||||||
|
if !data.is_null() {
|
||||||
|
unsafe { drop(Box::from_raw(data)) };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub extern "C" fn mize_write_stdout(str_ptr: *const c_char) {
|
||||||
|
let Some(s) = cstr(str_ptr) else { return };
|
||||||
|
let _ = write_to_mize_stdout(format_args!("{s}"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the thread-local error (like errno). `load_part` / C-callback wrappers turn this into `Err`.
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub extern "C" fn set_err(msg: *const c_char) {
|
||||||
|
let text = cstr(msg).unwrap_or("set_err called with null/invalid message");
|
||||||
|
CUR_ERR.with(|slot| {
|
||||||
|
*slot.borrow_mut() = Some(mize_err!("{}", text));
|
||||||
|
});
|
||||||
|
}
|
||||||
287
core/src/config.rs
Normal file
287
core/src/config.rs
Normal file
@@ -0,0 +1,287 @@
|
|||||||
|
use std::any::Any;
|
||||||
|
use std::fmt::Debug;
|
||||||
|
|
||||||
|
use crate::MizeResult;
|
||||||
|
|
||||||
|
use crate::get_global_mize;
|
||||||
|
use crate::id::MizeId;
|
||||||
|
use crate::item::{IntoItemData, ItemData};
|
||||||
|
use crate::Mize;
|
||||||
|
|
||||||
|
pub type ConfigFun = Box<dyn Fn() -> MizeResult<ItemData> + Send + Sync + 'static>;
|
||||||
|
pub type MizeFun = Box<dyn Fn(ItemData) -> MizeResult<ItemData> + Send + Sync + 'static>;
|
||||||
|
|
||||||
|
pub struct ConfigOpt {
|
||||||
|
pub name: String,
|
||||||
|
pub val: Option<ItemData>,
|
||||||
|
pub fun: Option<ConfigFun>,
|
||||||
|
pub deps: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait FromItemData {
|
||||||
|
fn from_itemdata(data: ItemData) -> MizeResult<Self> where Self: Sized;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Debug for ConfigOpt {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "({}: {})", self.name, self.val.clone().unwrap_or(ItemData::new()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct ConfigOptId {
|
||||||
|
pub id: MizeId,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ConfigOptId {
|
||||||
|
pub fn default_val(self, val: ItemData) -> Self {
|
||||||
|
let mize = get_global_mize().unwrap();
|
||||||
|
// in generate mode: record the default value as metadata, skip normal op
|
||||||
|
if mize.is_generate_mode().unwrap_or(false) {
|
||||||
|
mize.record_opt("_default_val", self.id.clone(), Some(val)).ok();
|
||||||
|
return self.clone();
|
||||||
|
}
|
||||||
|
let mut config_opts = mize.config_opts.lock().unwrap();
|
||||||
|
let opt = config_opts.get_mut(&self.id).unwrap();
|
||||||
|
opt.val = Some(val);
|
||||||
|
self.clone()
|
||||||
|
}
|
||||||
|
pub fn fun(self, fun: ConfigFun) -> Self {
|
||||||
|
let mize = get_global_mize().unwrap();
|
||||||
|
// in generate mode: record as an opt with a fun, skip normal op
|
||||||
|
if mize.is_generate_mode().unwrap_or(false) {
|
||||||
|
mize.record_opt("_fun", self.id.clone(), None).ok();
|
||||||
|
return self.clone();
|
||||||
|
}
|
||||||
|
let mut config_opts = mize.config_opts.lock().unwrap();
|
||||||
|
let opt = config_opts.get_mut(&self.id).unwrap();
|
||||||
|
opt.fun = Some(fun);
|
||||||
|
self.clone()
|
||||||
|
}
|
||||||
|
pub fn method<F: Fn(ItemData) -> MizeResult<ItemData> + Send + Sync + 'static>(self, name: &str, signature: &str, fun: F) -> ConfigOptId {
|
||||||
|
let mize = get_global_mize().unwrap();
|
||||||
|
let mut cur_part = self.id.clone();
|
||||||
|
let id = cur_part.join(name).unwrap();
|
||||||
|
// in generate mode: record method metadata, skip normal registration
|
||||||
|
if mize.is_generate_mode().unwrap_or(false) {
|
||||||
|
mize.record_method(name, id.clone(), "method", signature).ok();
|
||||||
|
return self.clone();
|
||||||
|
}
|
||||||
|
let mut opts = mize.config_opts.lock().unwrap();
|
||||||
|
let opt = ConfigOpt {
|
||||||
|
name: id.dot(),
|
||||||
|
val: None,
|
||||||
|
fun: None,
|
||||||
|
deps: Vec::new(),
|
||||||
|
};
|
||||||
|
let mut fun_values = mize.fun_values.lock().unwrap();
|
||||||
|
fun_values.insert(id.clone(), Box::new(fun));
|
||||||
|
opts.insert(id.clone(), opt);
|
||||||
|
|
||||||
|
self.clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//pub trait ToItemDataHelper {
|
||||||
|
//fn into_item_data(self) -> ItemData;
|
||||||
|
//}
|
||||||
|
|
||||||
|
pub trait MizeAny {
|
||||||
|
fn as_raw_ptr(&self) -> *const ();
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> MizeAny for T {
|
||||||
|
fn as_raw_ptr(&self) -> *const () {
|
||||||
|
self as *const Self as *const ()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Load the `.so` for `part_name`, but register it under `at_id`
|
||||||
|
/// (e.g. `load_part_at("priv.spacetime", "spacetime")`).
|
||||||
|
/// No-op in generate mode — bindings come from listing the part in `gen = [...]`.
|
||||||
|
pub fn load_part_at(at_id: &str, part_name: &str) -> MizeResult<()> {
|
||||||
|
let mize = get_global_mize()?;
|
||||||
|
if mize.is_generate_mode().unwrap_or(false) {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
crate::platform::load_part(part_name, None, Some(at_id))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn opt(name: &str) -> crate::config::ConfigOptId {
|
||||||
|
let mize = get_global_mize().unwrap();
|
||||||
|
let mut cur_part = mize.cur_eval_part.lock().unwrap().clone().unwrap();
|
||||||
|
let id = cur_part.join(name).unwrap();
|
||||||
|
// in generate mode: record opt metadata, skip normal registration
|
||||||
|
if mize.is_generate_mode().unwrap_or(false) {
|
||||||
|
mize.record_opt(name, id.clone(), None).ok();
|
||||||
|
return ConfigOptId { id };
|
||||||
|
}
|
||||||
|
let mut opts = mize.config_opts.lock().unwrap();
|
||||||
|
let opt = ConfigOpt {
|
||||||
|
name: id.dot(),
|
||||||
|
val: None,
|
||||||
|
fun: None,
|
||||||
|
deps: Vec::new(),
|
||||||
|
};
|
||||||
|
opts.insert(id.clone(), opt);
|
||||||
|
ConfigOptId { id }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn method<F: Fn(ItemData) -> MizeResult<ItemData> + Send + Sync + 'static>(name: &str, signature: &str, fun: F) -> ConfigOptId {
|
||||||
|
let mize = get_global_mize().unwrap();
|
||||||
|
let mut cur_part = mize.cur_eval_part.lock().unwrap().clone().unwrap();
|
||||||
|
let id = cur_part.join(name).unwrap();
|
||||||
|
// in generate mode: record method metadata, skip normal registration
|
||||||
|
if mize.is_generate_mode().unwrap_or(false) {
|
||||||
|
mize.record_method(name, id.clone(), "method", signature).ok();
|
||||||
|
return ConfigOptId { id };
|
||||||
|
}
|
||||||
|
let mut opts = mize.config_opts.lock().unwrap();
|
||||||
|
let opt = ConfigOpt {
|
||||||
|
name: id.dot(),
|
||||||
|
val: None,
|
||||||
|
fun: None,
|
||||||
|
deps: Vec::new(),
|
||||||
|
};
|
||||||
|
let mut fun_values = mize.fun_values.lock().unwrap();
|
||||||
|
fun_values.insert(id.clone(), Box::new(fun));
|
||||||
|
opts.insert(id.clone(), opt);
|
||||||
|
ConfigOptId { id }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn effect<F: Fn(ItemData) -> MizeResult<ItemData> + Send + Sync + 'static>(name: &str, fun: F) -> ConfigOptId {
|
||||||
|
let mize = get_global_mize().unwrap();
|
||||||
|
let mut cur_part = mize.cur_eval_part.lock().unwrap().clone().unwrap();
|
||||||
|
let id = cur_part.join(name).unwrap();
|
||||||
|
// in generate mode: record effect metadata, skip normal registration
|
||||||
|
if mize.is_generate_mode().unwrap_or(false) {
|
||||||
|
mize.record_method(name, id.clone(), "effect", "").ok();
|
||||||
|
return ConfigOptId { id };
|
||||||
|
}
|
||||||
|
let mut opts = mize.config_opts.lock().unwrap();
|
||||||
|
let opt = ConfigOpt {
|
||||||
|
name: id.dot(),
|
||||||
|
val: None,
|
||||||
|
fun: None,
|
||||||
|
deps: Vec::new(),
|
||||||
|
};
|
||||||
|
let mut fun_values = mize.fun_values.lock().unwrap();
|
||||||
|
fun_values.insert(id.clone(), Box::new(fun));
|
||||||
|
opts.insert(id.clone(), opt);
|
||||||
|
ConfigOptId { id }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn apply<F: Fn(ItemData) -> MizeResult<ItemData> + Send + Sync + 'static>(name: &str, fun: F) -> ConfigOptId {
|
||||||
|
let mize = get_global_mize().unwrap();
|
||||||
|
let mut cur_part = mize.cur_eval_part.lock().unwrap().clone().unwrap();
|
||||||
|
let id = cur_part.join(name).unwrap();
|
||||||
|
// in generate mode: record apply metadata, skip normal registration
|
||||||
|
if mize.is_generate_mode().unwrap_or(false) {
|
||||||
|
mize.record_method(name, id.clone(), "apply", "").ok();
|
||||||
|
return ConfigOptId { id };
|
||||||
|
}
|
||||||
|
let mut opts = mize.config_opts.lock().unwrap();
|
||||||
|
let opt = ConfigOpt {
|
||||||
|
name: id.dot(),
|
||||||
|
val: None,
|
||||||
|
fun: None,
|
||||||
|
deps: Vec::new(),
|
||||||
|
};
|
||||||
|
let mut fun_values = mize.fun_values.lock().unwrap();
|
||||||
|
fun_values.insert(id.clone(), Box::new(fun));
|
||||||
|
opts.insert(id.clone(), opt);
|
||||||
|
ConfigOptId { id }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn thread<F: Fn(ItemData) -> MizeResult<ItemData> + Send + Sync + 'static>(name: &str, fun: F) -> ConfigOptId {
|
||||||
|
let mize = get_global_mize().unwrap();
|
||||||
|
let mut cur_part = mize.cur_eval_part.lock().unwrap().clone().unwrap();
|
||||||
|
let id = cur_part.join(name).unwrap();
|
||||||
|
// in generate mode: record thread metadata, skip normal registration
|
||||||
|
if mize.is_generate_mode().unwrap_or(false) {
|
||||||
|
mize.record_method(name, id.clone(), "thread", "").ok();
|
||||||
|
return ConfigOptId { id };
|
||||||
|
}
|
||||||
|
let mut opts = mize.config_opts.lock().unwrap();
|
||||||
|
let opt = ConfigOpt {
|
||||||
|
name: id.dot(),
|
||||||
|
val: None,
|
||||||
|
fun: None,
|
||||||
|
deps: Vec::new(),
|
||||||
|
};
|
||||||
|
let mut fun_values = mize.fun_values.lock().unwrap();
|
||||||
|
fun_values.insert(id.clone(), Box::new(fun));
|
||||||
|
opts.insert(id.clone(), opt);
|
||||||
|
ConfigOptId { id }
|
||||||
|
}
|
||||||
|
|
||||||
|
type CFun = unsafe extern "C" fn (*mut ItemData) -> *mut ItemData;
|
||||||
|
|
||||||
|
pub mod c_api {
|
||||||
|
use std::ffi::c_char;
|
||||||
|
|
||||||
|
use crate::config::{CFun, ConfigOptId};
|
||||||
|
use std::ffi::CStr;
|
||||||
|
|
||||||
|
pub extern "C" fn opt(name: *const c_char) -> ConfigOptId {
|
||||||
|
unsafe {
|
||||||
|
let c_str = CStr::from_ptr(name);
|
||||||
|
let string = c_str.to_string_lossy();
|
||||||
|
super::opt(&string)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub extern "C" fn method(name: *const c_char, signature: *const c_char, c_fun: CFun) -> ConfigOptId {
|
||||||
|
unsafe {
|
||||||
|
let name_str = CStr::from_ptr(name).to_string_lossy();
|
||||||
|
let sig_str = if signature.is_null() {
|
||||||
|
String::new()
|
||||||
|
} else {
|
||||||
|
CStr::from_ptr(signature).to_string_lossy().into_owned()
|
||||||
|
};
|
||||||
|
super::method(&name_str, &sig_str, move |args| {
|
||||||
|
let raw_ptr = Box::into_raw(Box::new(args));
|
||||||
|
let ret_ptr = c_fun(raw_ptr);
|
||||||
|
let data = *Box::from_raw(ret_ptr);
|
||||||
|
Ok(data)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub extern "C" fn effect(name: *const c_char, c_fun: CFun) -> ConfigOptId {
|
||||||
|
unsafe {
|
||||||
|
let c_str = CStr::from_ptr(name);
|
||||||
|
let string = c_str.to_string_lossy();
|
||||||
|
super::effect(&string, move |args| {
|
||||||
|
let raw_ptr = Box::into_raw(Box::new(args));
|
||||||
|
let ret_ptr = c_fun(raw_ptr);
|
||||||
|
let data = *Box::from_raw(ret_ptr);
|
||||||
|
Ok(data)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub extern "C" fn apply(name: *const c_char, c_fun: CFun) -> ConfigOptId {
|
||||||
|
unsafe {
|
||||||
|
let c_str = CStr::from_ptr(name);
|
||||||
|
let string = c_str.to_string_lossy();
|
||||||
|
super::apply(&string, move |args| {
|
||||||
|
let raw_ptr = Box::into_raw(Box::new(args));
|
||||||
|
let ret_ptr = c_fun(raw_ptr);
|
||||||
|
let data = *Box::from_raw(ret_ptr);
|
||||||
|
Ok(data)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub extern "C" fn thread(name: *const c_char, c_fun: CFun) -> ConfigOptId {
|
||||||
|
unsafe {
|
||||||
|
let c_str = CStr::from_ptr(name);
|
||||||
|
let string = c_str.to_string_lossy();
|
||||||
|
super::thread(&string, move |args| {
|
||||||
|
let raw_ptr = Box::into_raw(Box::new(args));
|
||||||
|
let ret_ptr = c_fun(raw_ptr);
|
||||||
|
let data = *Box::from_raw(ret_ptr);
|
||||||
|
Ok(data)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,6 @@
|
|||||||
use colored::Colorize;
|
use colored::Colorize;
|
||||||
|
#[cfg(feature = "target-js")]
|
||||||
|
use wasm_bindgen::JsValue;
|
||||||
use core::result::Result;
|
use core::result::Result;
|
||||||
use core::result::Result::Ok;
|
use core::result::Result::Ok;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
@@ -7,13 +9,13 @@ use std::fmt::Debug;
|
|||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::string::FromUtf8Error;
|
use std::string::FromUtf8Error;
|
||||||
use tracing::{debug, error, info, trace, warn};
|
use crate::prelude::*;
|
||||||
|
|
||||||
//use crate::proto::MizeMessage;
|
//use crate::proto::MizeMessage;
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! mize_err {
|
macro_rules! mize_err {
|
||||||
($($arg:tt)*) => { MizeError::new().msg(format!( $($arg)*)) };
|
($($arg:tt)*) => { $crate::error::MizeError::new().msg(format!( $($arg)*)) };
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type MizeResult<T> = Result<T, MizeError>;
|
pub type MizeResult<T> = Result<T, MizeError>;
|
||||||
@@ -24,8 +26,8 @@ pub trait MizeResultTrait<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub trait IntoMizeResult<T, S> {
|
pub trait IntoMizeResult<T, S> {
|
||||||
fn mize_result(self) -> MizeResult<T>;
|
fn mres(self) -> MizeResult<T>;
|
||||||
fn mize_result_msg(self, msg: S) -> MizeResult<T>
|
fn mres_msg(self, msg: S) -> MizeResult<T>
|
||||||
where
|
where
|
||||||
S: std::fmt::Display;
|
S: std::fmt::Display;
|
||||||
}
|
}
|
||||||
@@ -125,16 +127,16 @@ impl MizeError {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn log(self) -> MizeError {
|
pub fn log(self) -> MizeError {
|
||||||
error!("MizeError envountered!");
|
println!("[ {} ] MizeError encountered!", "ERROR".red());
|
||||||
|
|
||||||
if let Some(ref location) = self.code_location {
|
if let Some(ref location) = self.code_location {
|
||||||
error!("[ {} ] {}", "LOCATION".yellow(), location);
|
println!("[ {} ] {}", "LOCATION".yellow(), location);
|
||||||
};
|
};
|
||||||
|
|
||||||
error!("[ {} ] {}", "BACKTRACE".yellow(), self.backtrace);
|
println!("[ {} ] {}", "BACKTRACE".yellow(), self.backtrace);
|
||||||
|
|
||||||
for msg in &self.messages {
|
for msg in &self.messages {
|
||||||
error!("[ {} ] {}", "MSG".yellow(), msg);
|
println!("[ {} ] {}", "MSG".yellow(), msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
self
|
self
|
||||||
@@ -261,7 +263,7 @@ where
|
|||||||
E: std::fmt::Display,
|
E: std::fmt::Display,
|
||||||
{
|
{
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
fn mize_result(self) -> MizeResult<T> {
|
fn mres(self) -> MizeResult<T> {
|
||||||
let caller_location = std::panic::Location::caller();
|
let caller_location = std::panic::Location::caller();
|
||||||
|
|
||||||
match self {
|
match self {
|
||||||
@@ -274,7 +276,7 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
fn mize_result_msg(self, msg: S) -> MizeResult<T>
|
fn mres_msg(self, msg: S) -> MizeResult<T>
|
||||||
where
|
where
|
||||||
S: std::fmt::Display,
|
S: std::fmt::Display,
|
||||||
{
|
{
|
||||||
@@ -346,3 +348,20 @@ impl From<MizeError> for MizeErrorWhichImplementsError {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl std::error::Error for MizeErrorWhichImplementsError {}
|
impl std::error::Error for MizeErrorWhichImplementsError {}
|
||||||
|
|
||||||
|
// i hate rust sometimes.
|
||||||
|
// why can't i impl IntoMizeResult for any type. and the extra concrete impl of it for JsValue overrides the more generic one.
|
||||||
|
#[cfg(feature = "target-js")]
|
||||||
|
pub trait IntoMizeResultJsValue<T> {
|
||||||
|
fn mres(self) -> MizeResult<T>;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "target-js")]
|
||||||
|
impl<T> IntoMizeResultJsValue<T> for Result<T, JsValue> {
|
||||||
|
fn mres(self) -> MizeResult<T> {
|
||||||
|
match self {
|
||||||
|
Ok(value) => Ok(value),
|
||||||
|
Err(err) => Err(mize_err!("From JsValue: {:?}", err)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,17 +1,11 @@
|
|||||||
use core::fmt;
|
use core::fmt;
|
||||||
use interner::{
|
use interner::{
|
||||||
shared::{SharedPool, SharedString, SharedVecString},
|
shared::{SharedString, SharedVecString},
|
||||||
Pooled,
|
|
||||||
};
|
};
|
||||||
use std::collections::hash_map::RandomState;
|
use std::hash::Hash;
|
||||||
use std::path::Path;
|
|
||||||
use std::{collections::binary_heap::Iter, fmt::write, hash::Hash};
|
|
||||||
|
|
||||||
use crate::error::{MizeError, MizeResult, MizeResultTrait};
|
use crate::{get_global_mize, error::MizeResult};
|
||||||
use crate::{
|
use crate::{instance::Mize, mize_err};
|
||||||
instance::{store::Store, Mize},
|
|
||||||
mize_err,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Hash, Eq, PartialEq)]
|
#[derive(Debug, Clone, Hash, Eq, PartialEq)]
|
||||||
pub struct MizeId {
|
pub struct MizeId {
|
||||||
@@ -38,6 +32,24 @@ pub trait IntoMizeId {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl MizeId {
|
impl MizeId {
|
||||||
|
pub fn dot(&self) -> String {
|
||||||
|
let path = self.path();
|
||||||
|
let mut new_path = Vec::new();
|
||||||
|
for el in path.iter() {
|
||||||
|
new_path.push(el.to_owned());
|
||||||
|
}
|
||||||
|
new_path.join(".")
|
||||||
|
}
|
||||||
|
pub fn join(&mut self, to_add: &str) -> MizeResult<MizeId> {
|
||||||
|
let path = self.path();
|
||||||
|
let mut new_path = Vec::new();
|
||||||
|
for el in path.iter() {
|
||||||
|
new_path.push(el.to_owned());
|
||||||
|
}
|
||||||
|
new_path.push(to_add.to_owned());
|
||||||
|
let mize = get_global_mize()?;
|
||||||
|
Ok(new_path.to_mize_id(&mize)?)
|
||||||
|
}
|
||||||
pub fn store_part(&self) -> &str {
|
pub fn store_part(&self) -> &str {
|
||||||
self.path
|
self.path
|
||||||
.iter()
|
.iter()
|
||||||
@@ -59,7 +71,7 @@ impl MizeId {
|
|||||||
|
|
||||||
pub fn after_store_part(&self) -> Vec<String> {
|
pub fn after_store_part(&self) -> Vec<String> {
|
||||||
let tmp = self.path();
|
let tmp = self.path();
|
||||||
let mut path_iter = tmp.into_iter();
|
let mut path_iter = <Box<[std::string::String]> as Clone>::clone(&tmp).into_iter();
|
||||||
path_iter.next();
|
path_iter.next();
|
||||||
let new_path: Vec<String> = path_iter.map(|v| v.to_owned()).collect();
|
let new_path: Vec<String> = path_iter.map(|v| v.to_owned()).collect();
|
||||||
|
|
||||||
@@ -130,7 +142,7 @@ impl IntoMizeId for Vec<&str> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl IntoMizeId for MizeId {
|
impl IntoMizeId for MizeId {
|
||||||
fn to_mize_id(self, instance: &Mize) -> MizeResult<MizeId> {
|
fn to_mize_id(self, _instance: &Mize) -> MizeResult<MizeId> {
|
||||||
Ok(self)
|
Ok(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
61
core/src/include/mize.h
Normal file
61
core/src/include/mize.h
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mize C API — thin header over libmize.so exports (core/src/c_api.rs).
|
||||||
|
*
|
||||||
|
* Names for opt / method / thread / get_config / set_config are absolute
|
||||||
|
* dotted paths for now (e.g. "vicinae.open", "vicinae.nav").
|
||||||
|
*
|
||||||
|
* Errors: thread-local, like errno. Call set_err("...") from C; Rust
|
||||||
|
* (load_part / method wrappers) turns that into Err. Part entry is
|
||||||
|
* `void part_<name>(void)` — no sret / Result return.
|
||||||
|
*
|
||||||
|
* ItemData (`DATA`) is opaque. Ownership:
|
||||||
|
* - Callbacks receive a borrowed `args` (do not free).
|
||||||
|
* - Callbacks must return an owned DATA* (e.g. data_new()); may return NULL for empty.
|
||||||
|
* - get_config returns owned DATA*; free with data_free.
|
||||||
|
* - set_config takes ownership of the value pointer.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef struct ItemData DATA;
|
||||||
|
|
||||||
|
/** C method / thread / effect callback. args is borrowed; return owned DATA* or NULL. */
|
||||||
|
typedef DATA *(*CFun)(DATA *args);
|
||||||
|
|
||||||
|
void opt(const char *name);
|
||||||
|
|
||||||
|
void method(const char *name, const char *signature, CFun c_fun);
|
||||||
|
|
||||||
|
void effect(const char *name, CFun c_fun);
|
||||||
|
|
||||||
|
void thread(const char *name, CFun c_fun);
|
||||||
|
|
||||||
|
void set_config(const char *name, DATA *value);
|
||||||
|
|
||||||
|
DATA *get_config(const char *name);
|
||||||
|
|
||||||
|
DATA *data_new(void);
|
||||||
|
|
||||||
|
/** Store an opaque pointer in the native-value registry. */
|
||||||
|
DATA *data_from_native(void *ptr);
|
||||||
|
|
||||||
|
/** Read back a pointer previously stored with data_from_native. Returns NULL on error. */
|
||||||
|
void *data_as_native(DATA *data);
|
||||||
|
|
||||||
|
void data_free(DATA *data);
|
||||||
|
|
||||||
|
void mize_write_stdout(const char *str);
|
||||||
|
|
||||||
|
/** Set the thread-local error message (consumed by Rust after the call). */
|
||||||
|
void set_err(const char *msg);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
@@ -41,9 +41,9 @@ pub fn value_raw_from_peer(item: &mut Item) -> MizeResult<ItemData> {
|
|||||||
let conn_id_str = id.nth_part(2)?;
|
let conn_id_str = id.nth_part(2)?;
|
||||||
let conn_id: u64 = conn_id_str.parse()?;
|
let conn_id: u64 = conn_id_str.parse()?;
|
||||||
let tmp_path = id.path();
|
let tmp_path = id.path();
|
||||||
let mut new_id_str = tmp_path.into_iter();
|
let mut new_id_str = tmp_path.iter();
|
||||||
let skipped = new_id_str.skip(4);
|
let skipped = new_id_str.skip(4);
|
||||||
let new_id = item.instance.new_id(skipped.collect::<Vec<&String>>())?;
|
let new_id = item.instance.new_id(skipped.cloned().collect::<Vec<String>>())?;
|
||||||
|
|
||||||
let connection = item.instance.get_connection(conn_id)?;
|
let connection = item.instance.get_connection(conn_id)?;
|
||||||
|
|
||||||
@@ -1,32 +1,29 @@
|
|||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use colored::Colorize;
|
use colored::Colorize;
|
||||||
use core::fmt;
|
use core::fmt;
|
||||||
use flume::{bounded, unbounded, Receiver, Sender};
|
use std::time::Duration;
|
||||||
|
use flume::{bounded, unbounded, Sender};
|
||||||
use interner::shared::{StringPool, VecStringPool};
|
use interner::shared::{StringPool, VecStringPool};
|
||||||
use mize_macros::mize_part;
|
|
||||||
use std::any::Any;
|
use std::any::Any;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::fs::create_dir;
|
|
||||||
use std::fs::File;
|
|
||||||
use std::ops::{Deref, DerefMut};
|
use std::ops::{Deref, DerefMut};
|
||||||
use std::path::{Path, PathBuf};
|
use std::sync::{Arc, Mutex, RwLock};
|
||||||
use std::sync::{Arc, Mutex};
|
|
||||||
use std::{thread, vec};
|
use std::{thread, vec};
|
||||||
use tracing::{debug, error, info, trace, warn, Instrument};
|
|
||||||
use uuid::Uuid;
|
|
||||||
|
|
||||||
use crate::config::ConfigOptNameAndMize;
|
use crate::set_global_mize;
|
||||||
use crate::config::{gather_config, ConfigOpt};
|
use crate::config::{ConfigOpt, ConfigOptId, MizeFun};
|
||||||
use crate::error::{IntoMizeResult, MizeError, MizeResult, MizeResultTrait};
|
use crate::error::{MizeError, MizeResult};
|
||||||
use crate::id::{IntoMizeId, MizeId, Namespace};
|
use crate::id::{IntoMizeId, MizeId, Namespace};
|
||||||
use crate::instance::store::Store;
|
use crate::instance::store::Store;
|
||||||
use crate::instance::subscription::Subscription;
|
use crate::instance::subscription::Subscription;
|
||||||
use crate::instance::updater::Operation;
|
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::item::{Item, ItemData};
|
||||||
use crate::memstore::MemStore;
|
use crate::memstore::MemStore;
|
||||||
|
use crate::mize_err;
|
||||||
use crate::proto::MizeMessage;
|
use crate::proto::MizeMessage;
|
||||||
use crate::{mize_err, Module};
|
use crate::error::IntoMizeResult;
|
||||||
|
use crate::prelude::*;
|
||||||
|
|
||||||
use self::connection::{ConnListener, Connection};
|
use self::connection::{ConnListener, Connection};
|
||||||
use self::updater::handle_operation;
|
use self::updater::handle_operation;
|
||||||
@@ -40,7 +37,6 @@ use core::future::Future;
|
|||||||
use std::thread::JoinHandle;
|
use std::thread::JoinHandle;
|
||||||
|
|
||||||
pub mod connection;
|
pub mod connection;
|
||||||
pub mod module;
|
|
||||||
pub mod msg_thread;
|
pub mod msg_thread;
|
||||||
pub mod store;
|
pub mod store;
|
||||||
pub mod subscription;
|
pub mod subscription;
|
||||||
@@ -51,7 +47,7 @@ mod tests;
|
|||||||
|
|
||||||
static MSG_CHANNEL_SIZE: usize = 200;
|
static MSG_CHANNEL_SIZE: usize = 200;
|
||||||
|
|
||||||
static BUILD_TIME_CONFIG: &str = include_str!(std::env!("MIZE_BUILD_CONFIG"));
|
static BUILD_TIME_CONFIG: &str = std::env!("MIZE_BUILD_CONFIG");
|
||||||
|
|
||||||
/// The Instance type is the heart of the mize system
|
/// The Instance type is the heart of the mize system
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@@ -61,15 +57,18 @@ pub struct Mize {
|
|||||||
connections: Arc<Mutex<Vec<Connection>>>,
|
connections: Arc<Mutex<Vec<Connection>>>,
|
||||||
next_con_id: Arc<Mutex<u64>>,
|
next_con_id: Arc<Mutex<u64>>,
|
||||||
subs: Arc<Mutex<HashMap<MizeId, Vec<Subscription>>>>,
|
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) id_pool: Arc<Mutex<VecStringPool>>,
|
||||||
pub(crate) namespace_pool: Arc<Mutex<StringPool>>,
|
pub(crate) namespace_pool: Arc<Mutex<StringPool>>,
|
||||||
|
|
||||||
pub(crate) parts: Arc<Mutex<HashMap<&'static str, Option<Box<dyn MizePart + Sync + Send>>>>>,
|
pub(crate) listeners: Arc<
|
||||||
|
Mutex<HashMap<String, Arc<dyn Fn(MizeMessage, &mut Mize) -> MizeResult<()> + Send + Sync>>>,
|
||||||
|
>,
|
||||||
|
|
||||||
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
|
// the namespace the instance operates in
|
||||||
pub(crate) namespace: Arc<Mutex<Namespace>>,
|
pub(crate) namespace: Arc<Mutex<Namespace>>,
|
||||||
@@ -83,133 +82,127 @@ pub struct Mize {
|
|||||||
give_msg_wait: Arc<Mutex<HashMap<MizeId, Vec<Sender<ItemData>>>>>,
|
give_msg_wait: Arc<Mutex<HashMap<MizeId, Vec<Sender<ItemData>>>>>,
|
||||||
create_msg_wait: Arc<Mutex<Option<Sender<MizeId>>>>,
|
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")]
|
#[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 applies: 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", "apply", "thread"
|
||||||
|
/// Raw signature string, e.g. `"String -> ()"` or `"Command, Func -> ()"`.
|
||||||
|
pub signature: String,
|
||||||
|
/// Parsed argument type names (left of `->`).
|
||||||
|
pub arg_types: Vec<String>,
|
||||||
|
/// Parsed return type name (right of `->`). Empty means unspecified / ItemData.
|
||||||
|
pub return_type: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Parse a method signature like `"String -> ()"` or `"Command, Func -> ()"`.
|
||||||
|
/// Returns `(arg_types, return_type)` where `return_type` is empty if no `->` was given.
|
||||||
|
pub fn parse_method_signature(signature: &str) -> (Vec<String>, String) {
|
||||||
|
let signature = signature.trim();
|
||||||
|
if signature.is_empty() {
|
||||||
|
return (Vec::new(), String::new());
|
||||||
|
}
|
||||||
|
let (args_part, ret_part) = match signature.split_once("->") {
|
||||||
|
Some((a, r)) => (a.trim(), r.trim().to_string()),
|
||||||
|
None => (signature, String::new()),
|
||||||
|
};
|
||||||
|
let arg_types = if args_part.is_empty() {
|
||||||
|
Vec::new()
|
||||||
|
} else {
|
||||||
|
args_part
|
||||||
|
.split(',')
|
||||||
|
.map(|s| s.trim().to_string())
|
||||||
|
.filter(|s| !s.is_empty())
|
||||||
|
.collect()
|
||||||
|
};
|
||||||
|
(arg_types, ret_part)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct NativeMetadata {
|
||||||
|
pub name: String,
|
||||||
|
pub type_name: String,
|
||||||
|
pub associated: Vec<String>,
|
||||||
|
pub methods: Vec<String>,
|
||||||
|
/// Signature strings parallel to `associated`.
|
||||||
|
pub associated_sigs: Vec<String>,
|
||||||
|
/// Signature strings parallel to `methods`.
|
||||||
|
pub method_sigs: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
pub struct InstanceRef {
|
pub struct InstanceRef {
|
||||||
inner: Arc<Mutex<Mize>>,
|
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) -> MizeResult<MizeId> {
|
||||||
|
Ok(self.id.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait PartToNative {
|
||||||
|
fn from_mize_id(id: MizeId) -> Self;
|
||||||
|
}
|
||||||
|
|
||||||
pub struct InstanceAsync {
|
pub struct InstanceAsync {
|
||||||
inner: Arc<Mutex<Mize>>,
|
inner: Arc<Mutex<Mize>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct DynMizePartGuard {
|
pub trait MizePart {
|
||||||
pub mize: Mize,
|
fn id(&self) -> MizeResult<MizeId>;
|
||||||
pub part: Option<Box<dyn MizePart + Send + Sync>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct MizePartGuard<T: MizePart + Send + Sync + 'static> {
|
|
||||||
pub mize: Mize,
|
|
||||||
pub part: Option<T>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: MizePart + Send + Sync> Deref for MizePartGuard<T> {
|
|
||||||
type Target = T;
|
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
&*self.part.as_ref().unwrap()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: MizePart + Send + Sync> DerefMut for MizePartGuard<T> {
|
|
||||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
|
||||||
&mut *self.part.as_mut().unwrap()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: MizePart + Send + Sync> Drop for MizePartGuard<T> {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
if self.part.is_none() {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let part = self.part.take().unwrap();
|
|
||||||
self.mize.give_back_part(Box::new(part));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Deref for DynMizePartGuard {
|
|
||||||
type Target = dyn MizePart + Send + Sync;
|
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
&*self.part.as_deref().unwrap()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DerefMut for DynMizePartGuard {
|
|
||||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
|
||||||
&mut *self.part.as_deref_mut().unwrap()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Drop for DynMizePartGuard {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
if self.part.is_none() {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
self.mize.give_back_part(self.part.take().unwrap());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait MizePartCreateGenerated: Sized {
|
|
||||||
fn create_generated(mize: Mize) -> Self;
|
|
||||||
}
|
|
||||||
pub trait MizePartGenerated {
|
|
||||||
fn name_generated(&self) -> &'static str;
|
|
||||||
fn get_mize_generated(&mut self) -> &mut Mize;
|
|
||||||
fn as_any_generated(&self) -> &dyn Any;
|
|
||||||
fn as_any_mut_generated(&mut self) -> &mut dyn Any;
|
|
||||||
fn into_any_generated(self: Box<Self>) -> Box<dyn Any>;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait MizePartCreate: MizePartCreateGenerated + MizePart + 'static {
|
|
||||||
fn create(mize: &mut Mize) -> Box<dyn MizePart> {
|
|
||||||
Box::new(Self::create_generated(mize.clone()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
pub trait MizePart: MizePartGenerated {
|
|
||||||
fn name(&self) -> &'static str {
|
|
||||||
self.name_generated()
|
|
||||||
}
|
|
||||||
fn get_mize(&mut self) -> &mut Mize {
|
|
||||||
self.get_mize_generated()
|
|
||||||
}
|
|
||||||
fn as_any(&self) -> &dyn Any {
|
|
||||||
self.as_any_generated()
|
|
||||||
}
|
|
||||||
fn as_any_mut(&mut self) -> &mut dyn Any {
|
|
||||||
self.as_any_mut_generated()
|
|
||||||
}
|
|
||||||
fn into_any(self: Box<Self>) -> Box<dyn Any> {
|
|
||||||
self.into_any_generated()
|
|
||||||
}
|
|
||||||
fn init(&mut self, mize: &mut Mize) -> MizeResult<()> {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
fn run(&mut self, mize: &mut Mize) -> MizeResult<()> {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
fn deps(&self) -> &'static [&'static str] {
|
|
||||||
&[]
|
|
||||||
}
|
|
||||||
fn opts(&self, mize: &mut Mize) {
|
|
||||||
()
|
|
||||||
}
|
|
||||||
async fn async_run(&mut self, mize: &mut Mize) -> MizeResult<()> {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
async fn async_init(&mut self, mize: &mut Mize) -> MizeResult<()> {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Mize {
|
impl Mize {
|
||||||
pub fn empty() -> MizeResult<Mize> {
|
pub fn empty() -> MizeResult<Mize> {
|
||||||
|
let runtime = tokio::runtime::Builder::new_multi_thread().enable_all().build()?;
|
||||||
|
let guard = runtime.enter();
|
||||||
|
std::mem::forget(guard);
|
||||||
|
|
||||||
let id_pool = Arc::new(Mutex::new(VecStringPool::default()));
|
let id_pool = Arc::new(Mutex::new(VecStringPool::default()));
|
||||||
let namespace_pool_raw = StringPool::default();
|
let namespace_pool_raw = StringPool::default();
|
||||||
let connections = Arc::new(Mutex::new(Vec::new()));
|
let connections = Arc::new(Mutex::new(Vec::new()));
|
||||||
@@ -229,6 +222,7 @@ impl Mize {
|
|||||||
parts: Arc::new(Mutex::new(HashMap::new())),
|
parts: Arc::new(Mutex::new(HashMap::new())),
|
||||||
part_names: Arc::new(Mutex::new(Vec::new())),
|
part_names: Arc::new(Mutex::new(Vec::new())),
|
||||||
config_opts: Arc::new(Mutex::new(HashMap::new())),
|
config_opts: Arc::new(Mutex::new(HashMap::new())),
|
||||||
|
listeners: Arc::new(Mutex::new(HashMap::new())),
|
||||||
connections,
|
connections,
|
||||||
subs,
|
subs,
|
||||||
id_pool,
|
id_pool,
|
||||||
@@ -236,17 +230,21 @@ impl Mize {
|
|||||||
self_namespace,
|
self_namespace,
|
||||||
op_tx,
|
op_tx,
|
||||||
namespace_pool: Arc::new(Mutex::new(namespace_pool_raw)),
|
namespace_pool: Arc::new(Mutex::new(namespace_pool_raw)),
|
||||||
modules: Arc::new(Mutex::new(HashMap::new())),
|
|
||||||
threads: Arc::new(Mutex::new(Vec::new())),
|
threads: Arc::new(Mutex::new(Vec::new())),
|
||||||
next_thread_id: Arc::new(Mutex::new(0)),
|
next_thread_id: Arc::new(Mutex::new(0)),
|
||||||
next_con_id: Arc::new(Mutex::new(1)),
|
next_con_id: Arc::new(Mutex::new(1)),
|
||||||
give_msg_wait,
|
give_msg_wait,
|
||||||
create_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")]
|
#[cfg(feature = "async")]
|
||||||
runtime: Arc::new(Mutex::new(
|
runtime: Arc::new(Mutex::new(runtime)),
|
||||||
Runtime::new().mize_result_msg("Could not create async runtime")?,
|
|
||||||
)),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(feature = "target-os")]
|
#[cfg(feature = "target-os")]
|
||||||
@@ -282,7 +280,7 @@ impl Mize {
|
|||||||
console_log!("before loading build time config");
|
console_log!("before loading build time config");
|
||||||
|
|
||||||
// load the config from build time
|
// load the config from build time
|
||||||
let config = ItemData::from_toml(BUILD_TIME_CONFIG)?;
|
let config = ItemData::from_json(BUILD_TIME_CONFIG.to_owned())?;
|
||||||
instance.set_blocking("0", config);
|
instance.set_blocking("0", config);
|
||||||
|
|
||||||
#[cfg(feature = "target-wasm ")]
|
#[cfg(feature = "target-wasm ")]
|
||||||
@@ -306,60 +304,63 @@ impl Mize {
|
|||||||
return Ok(instance);
|
return Ok(instance);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_part(&mut self, name: &str) -> MizeResult<DynMizePartGuard> {
|
pub fn id_new_dots(&mut self, name: &str) -> MizeResult<MizeId> {
|
||||||
match self.parts.lock().unwrap().get_mut(name) {
|
let parts = name.split(".").map(|v| v.to_owned()).collect::<Vec<String>>();
|
||||||
Some(part) => Ok(DynMizePartGuard {
|
self.new_id(parts)
|
||||||
part: Some(part.take().unwrap()),
|
|
||||||
mize: self.clone(),
|
|
||||||
}),
|
|
||||||
None => Err(mize_err!("Part '{name}' not found or currently taken")),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn has_part(&mut self, name: &str) -> bool {
|
|
||||||
self.parts.lock().unwrap().contains_key(name)
|
|
||||||
}
|
|
||||||
pub fn add_part(&mut self, part: Box<dyn MizePart + Send + Sync>) -> MizeResult<()> {
|
|
||||||
self.part_names.lock().unwrap().push(part.name());
|
|
||||||
self.parts.lock().unwrap().insert(part.name(), Some(part));
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
pub fn register_part(&mut self, part: Box<dyn MizePart + Send + Sync>) -> MizeResult<()> {
|
|
||||||
self.part_names.lock().unwrap().push(part.name());
|
|
||||||
self.parts.lock().unwrap().insert(part.name(), Some(part));
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
fn part_names(&mut self) -> Vec<&'static str> {
|
|
||||||
self.part_names.lock().unwrap().clone()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn give_back_part(&mut self, part: Box<dyn MizePart + Send + Sync>) {
|
pub fn get_part(&mut self, name: &str) -> MizeResult<PartRef> {
|
||||||
self.parts.lock().unwrap().insert(part.name(), Some(part));
|
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<()> {
|
pub fn init(&mut self) -> MizeResult<()> {
|
||||||
// gather options
|
|
||||||
gather_config(self)?;
|
// set global mize
|
||||||
|
set_global_mize(self.clone())?;
|
||||||
|
|
||||||
// platform specific init code
|
// platform specific init code
|
||||||
crate::platform::any::instance_init(self)?;
|
crate::platform::init(self)?;
|
||||||
|
|
||||||
// end of platform specific init code
|
// end of platform specific init code
|
||||||
|
|
||||||
// load the modules, ad specified in the load_modules config
|
// load the parts, as specified in the parts config
|
||||||
match self.get("0/config/load_modules")?.value_string() {
|
// Prefer `parts` when it is an array; otherwise `parts.load` (so `parts.workspaces` /
|
||||||
Ok(modules_to_load) => {
|
// `parts.dist.cd` can coexist under a `parts` table in TOML).
|
||||||
for module in modules_to_load.split(" ") {
|
let parts_to_load = match self.get_config("parts") {
|
||||||
#[cfg(feature = "target-wasm ")]
|
Ok(data) if data.cbor().as_array().is_some() => Some(data),
|
||||||
console_log!("loading module in Instance::init() ... {}", module);
|
_ => self.get_config("parts.load").ok(),
|
||||||
self.load_module(module)?;
|
};
|
||||||
|
if let Some(parts_to_load) = parts_to_load {
|
||||||
|
for part_name in parts_to_load
|
||||||
|
.cbor()
|
||||||
|
.as_array()
|
||||||
|
.ok_or("config parts/parts.load 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
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
debug!("INSTANCE INIT DONE");
|
debug!("MIZE INIT DONE");
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -367,13 +368,34 @@ impl Mize {
|
|||||||
trace!("[ {} ] Instance::with_config()", "CALL".yellow());
|
trace!("[ {} ] Instance::with_config()", "CALL".yellow());
|
||||||
trace!("config: {}", config);
|
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<()> {
|
pub fn migrate_to_store(&self, new_store: Box<dyn Store>) -> MizeResult<()> {
|
||||||
@@ -399,7 +421,7 @@ impl Mize {
|
|||||||
pub fn new_item(&self) -> MizeResult<Item> {
|
pub fn new_item(&self) -> MizeResult<Item> {
|
||||||
if self.get_namespace()? != self.get_self_namespace()? {
|
if self.get_namespace()? != self.get_self_namespace()? {
|
||||||
// need to send create msg and wait for it
|
// 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);
|
let msg = MizeMessage::new_create(connection.id);
|
||||||
|
|
||||||
@@ -412,7 +434,6 @@ impl Mize {
|
|||||||
drop(msg_wait_inner);
|
drop(msg_wait_inner);
|
||||||
|
|
||||||
let id = rx.recv()?;
|
let id = rx.recv()?;
|
||||||
println!("new_item namespace: {:?}", id.namespace());
|
|
||||||
|
|
||||||
return Ok(Item::new(id, self));
|
return Ok(Item::new(id, self));
|
||||||
}
|
}
|
||||||
@@ -515,29 +536,125 @@ impl Mize {
|
|||||||
Ok(id)
|
Ok(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn load_module(&mut self, name: &str) -> MizeResult<()> {
|
pub fn load_part(&mut self, name: &str) -> MizeResult<()> {
|
||||||
crate::platform::any::load_module(self, name, None)
|
tracing::debug!("loading part: {name}");
|
||||||
|
crate::platform::load_part(name, None, None)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn fetch_module(&mut self, name: &str) -> MizeResult<String> {
|
pub fn fetch_part(&mut self, name: &str) -> MizeResult<String> {
|
||||||
// platform specific way to fetch a module
|
// platform specific way to fetch a part
|
||||||
crate::platform::any::fetch_module(self, name)
|
crate::platform::fetch_part(self, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn load_module_at(&mut self, name: &str, path: String) -> MizeResult<()> {
|
pub fn load_part_from_path(&mut self, name: &str, path: String) -> MizeResult<()> {
|
||||||
// platform specific init code
|
crate::platform::load_part(name, Some(path), None)
|
||||||
crate::platform::any::load_module(self, name, Some(path))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_module(&mut self, name: &str) -> MizeResult<Box<dyn Module + Send + Sync>> {
|
// ===== generate mode accessors =====
|
||||||
let inner = self.modules.lock()?;
|
|
||||||
|
|
||||||
let module = inner
|
/// Enable or disable generate mode. When enabled, part_smth functions record
|
||||||
.get(name)
|
/// metadata (methods, opts, natives) instead of performing their normal operations.
|
||||||
.ok_or(mize_err!("Couldn't get_module('{name}')"))?
|
pub fn set_generate_mode(&self, enabled: bool) -> MizeResult<()> {
|
||||||
.clone_module();
|
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, signature: &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 (arg_types, return_type) = parse_method_signature(signature);
|
||||||
|
let m = MethodMetadata {
|
||||||
|
name: name.to_string(),
|
||||||
|
id,
|
||||||
|
kind: kind.to_string(),
|
||||||
|
signature: signature.to_string(),
|
||||||
|
arg_types,
|
||||||
|
return_type,
|
||||||
|
};
|
||||||
|
match kind {
|
||||||
|
"effect" => metadata.effects.push(m),
|
||||||
|
"apply" => metadata.applies.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>,
|
||||||
|
associated_sigs: Vec<String>,
|
||||||
|
method_sigs: 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,
|
||||||
|
associated_sigs,
|
||||||
|
method_sigs,
|
||||||
|
});
|
||||||
|
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> {
|
pub fn namespace_from_string(&self, ns_str: String) -> MizeResult<Namespace> {
|
||||||
@@ -554,12 +671,12 @@ impl Mize {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_namespace(&self) -> MizeResult<Namespace> {
|
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());
|
return Ok(namespace_inner.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_self_namespace(&self) -> MizeResult<Namespace> {
|
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());
|
return Ok(self_namespace_inner.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -567,8 +684,8 @@ impl Mize {
|
|||||||
Ok(self.get_namespace()? == self.get_self_namespace()?)
|
Ok(self.get_namespace()? == self.get_self_namespace()?)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_listener<T: ConnListener + 'static>(&mut self, listener: T) -> MizeResult<()> {
|
pub fn add_conn_listener<T: ConnListener + 'static>(&mut self, listener: T) -> MizeResult<()> {
|
||||||
let mut instance_clone = self.clone();
|
let instance_clone = self.clone();
|
||||||
self.spawn_background("some_listener", move || listener.listen(instance_clone));
|
self.spawn_background("some_listener", move || listener.listen(instance_clone));
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -593,7 +710,7 @@ impl Mize {
|
|||||||
|
|
||||||
let ns_of_peer_str = self
|
let ns_of_peer_str = self
|
||||||
.get(format!(
|
.get(format!(
|
||||||
"inst/con_by_id/{}/peer/0/config/namespace",
|
"inst/con_by_id/{}/peer/self/config/namespace",
|
||||||
conn_id
|
conn_id
|
||||||
))?
|
))?
|
||||||
.value_string()?;
|
.value_string()?;
|
||||||
@@ -617,6 +734,21 @@ impl Mize {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn report_err(&self, err: MizeError) {
|
pub fn report_err(&self, err: MizeError) {
|
||||||
|
#[cfg(feature = "target-js")] unsafe {
|
||||||
|
use wasm_bindgen::prelude::wasm_bindgen;
|
||||||
|
#[wasm_bindgen]
|
||||||
|
extern "C" {
|
||||||
|
#[wasm_bindgen(js_namespace = console)]
|
||||||
|
fn log(s: &str);
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! console_log {
|
||||||
|
// Note that this is using the `log` function imported above during
|
||||||
|
// `bare_bones`
|
||||||
|
($($t:tt)*) => ({ log(&format_args!($($t)*).to_string())})
|
||||||
|
}
|
||||||
|
console_log!("reported_err: {:?}", err);
|
||||||
|
}
|
||||||
err.log();
|
err.log();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -634,11 +766,10 @@ impl Mize {
|
|||||||
"Connection with id {} not known to instance",
|
"Connection with id {} not known to instance",
|
||||||
conn_id
|
conn_id
|
||||||
));
|
));
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_connection(&self, conn_id: u64) -> MizeResult<Connection> {
|
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() {
|
for connection in conn_inner.iter() {
|
||||||
if connection.id == conn_id {
|
if connection.id == conn_id {
|
||||||
@@ -653,7 +784,7 @@ impl Mize {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_connection_by_ns(&self, ns: Namespace) -> MizeResult<Connection> {
|
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() {
|
for connection in conn_inner.iter() {
|
||||||
if connection.ns == Some(ns.clone()) {
|
if connection.ns == Some(ns.clone()) {
|
||||||
@@ -677,11 +808,11 @@ impl Mize {
|
|||||||
let mut next_thread_id = self.next_thread_id.lock()?;
|
let mut next_thread_id = self.next_thread_id.lock()?;
|
||||||
|
|
||||||
let my_thread_id_no_mutex_guard = *next_thread_id;
|
let my_thread_id_no_mutex_guard = *next_thread_id;
|
||||||
let thread_mutex = self.threads.clone();
|
let _thread_mutex = self.threads.clone();
|
||||||
let name_to_move = name.to_owned();
|
let name_to_move = name.to_owned();
|
||||||
let to_spawn = move || -> MizeResult<()> {
|
let to_spawn = move || -> MizeResult<()> {
|
||||||
debug!("spawning thread: {}", name_to_move);
|
debug!("spawning thread: {}", name_to_move);
|
||||||
let my_thread_id = my_thread_id_no_mutex_guard;
|
let _my_thread_id = my_thread_id_no_mutex_guard;
|
||||||
|
|
||||||
if let Err(err) = func() {
|
if let Err(err) = func() {
|
||||||
mize_clone.report_err(err);
|
mize_clone.report_err(err);
|
||||||
@@ -727,17 +858,23 @@ impl Mize {
|
|||||||
) -> MizeResult<()> {
|
) -> MizeResult<()> {
|
||||||
let mut threads_inner = self.threads.lock()?;
|
let mut threads_inner = self.threads.lock()?;
|
||||||
let mut next_thread_id = self.next_thread_id.lock()?;
|
let mut next_thread_id = self.next_thread_id.lock()?;
|
||||||
|
let tokio_handle = tokio::runtime::Handle::current();
|
||||||
|
|
||||||
let my_thread_id_no_mutex_guard = *next_thread_id;
|
let my_thread_id_no_mutex_guard = *next_thread_id;
|
||||||
let thread_mutex = self.threads.clone();
|
let _thread_mutex = self.threads.clone();
|
||||||
let name_to_move = name.to_owned();
|
let name_to_move = name.to_owned();
|
||||||
let to_spawn = move || -> MizeResult<()> {
|
let to_spawn = move || -> () {
|
||||||
debug!("spawning thread: {}", name_to_move);
|
debug!("spawning thread: {}", name_to_move);
|
||||||
let my_thread_id = my_thread_id_no_mutex_guard;
|
let guard = tokio_handle.enter();
|
||||||
|
std::mem::forget(guard);
|
||||||
|
let _my_thread_id = my_thread_id_no_mutex_guard;
|
||||||
|
|
||||||
func()?;
|
let res = func();
|
||||||
|
if let Err(e) = res {
|
||||||
|
e.log();
|
||||||
|
}
|
||||||
|
|
||||||
let mut threads_inner = thread_mutex.lock()?;
|
//let mut threads_inner = thread_mutex.lock()?;
|
||||||
/*
|
/*
|
||||||
*threads_inner = threads_inner
|
*threads_inner = threads_inner
|
||||||
.clone()
|
.clone()
|
||||||
@@ -749,7 +886,6 @@ impl Mize {
|
|||||||
.collect();
|
.collect();
|
||||||
*/
|
*/
|
||||||
debug!("thread '{}' stopped", name_to_move);
|
debug!("thread '{}' stopped", name_to_move);
|
||||||
Ok(())
|
|
||||||
};
|
};
|
||||||
|
|
||||||
*next_thread_id += 1;
|
*next_thread_id += 1;
|
||||||
@@ -759,7 +895,7 @@ impl Mize {
|
|||||||
|
|
||||||
threads_inner.push((*next_thread_id, name.to_owned(), None));
|
threads_inner.push((*next_thread_id, name.to_owned(), None));
|
||||||
|
|
||||||
#[cfg(feature = "target-wasm ")]
|
#[cfg(feature = "target-js")]
|
||||||
{
|
{
|
||||||
//console_log!("in instance::spawn with wasm target")
|
//console_log!("in instance::spawn with wasm target")
|
||||||
}
|
}
|
||||||
@@ -803,8 +939,8 @@ impl Mize {
|
|||||||
let mut next_thread_id = self.next_thread_id.lock()?;
|
let mut next_thread_id = self.next_thread_id.lock()?;
|
||||||
|
|
||||||
threads_inner.push((*next_thread_id, name.to_owned(), None));
|
threads_inner.push((*next_thread_id, name.to_owned(), None));
|
||||||
let runtime_inner = self.runtime.lock().unwrap();
|
let runtime = tokio::runtime::Handle::current();
|
||||||
runtime_inner.spawn(func);
|
runtime.spawn(func);
|
||||||
|
|
||||||
*next_thread_id += 1;
|
*next_thread_id += 1;
|
||||||
|
|
||||||
@@ -813,9 +949,7 @@ impl Mize {
|
|||||||
|
|
||||||
#[cfg(feature = "async")]
|
#[cfg(feature = "async")]
|
||||||
pub fn async_get_handle(&self) -> Handle {
|
pub fn async_get_handle(&self) -> Handle {
|
||||||
let runtime_inner = self.runtime.lock().unwrap();
|
tokio::runtime::Handle::current()
|
||||||
let handle = runtime_inner.handle().to_owned();
|
|
||||||
handle
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "async")]
|
#[cfg(feature = "async")]
|
||||||
@@ -836,15 +970,13 @@ impl Mize {
|
|||||||
.expect("mutex lock failed in spawn_async_blocking");
|
.expect("mutex lock failed in spawn_async_blocking");
|
||||||
|
|
||||||
threads_inner.push((*next_thread_id, name.to_owned(), None));
|
threads_inner.push((*next_thread_id, name.to_owned(), None));
|
||||||
let runtime_inner = self.runtime.lock().unwrap();
|
let runtime = tokio::runtime::Handle::current();
|
||||||
let handle = runtime_inner.handle().to_owned();
|
|
||||||
*next_thread_id += 1;
|
*next_thread_id += 1;
|
||||||
|
|
||||||
drop(runtime_inner);
|
|
||||||
drop(threads_inner);
|
drop(threads_inner);
|
||||||
drop(next_thread_id);
|
drop(next_thread_id);
|
||||||
|
|
||||||
let result = handle.block_on(func);
|
let result = runtime.block_on(func);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -852,7 +984,7 @@ impl Mize {
|
|||||||
pub fn wait(&self) {
|
pub fn wait(&self) {
|
||||||
info!("Instance main thread waiting");
|
info!("Instance main thread waiting");
|
||||||
loop {
|
loop {
|
||||||
thread::sleep_ms(10000000)
|
thread::sleep(Duration::from_secs(100000))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -863,108 +995,83 @@ impl Mize {
|
|||||||
pub fn get_config(&mut self, name: &str) -> MizeResult<ItemData> {
|
pub fn get_config(&mut self, name: &str) -> MizeResult<ItemData> {
|
||||||
let mut config_opts = self.config_opts.lock().unwrap();
|
let mut config_opts = self.config_opts.lock().unwrap();
|
||||||
|
|
||||||
let opt = config_opts
|
let mut id;
|
||||||
.get_mut(name)
|
let mut split = name.split(".");
|
||||||
.ok_or_else(|| mize_err!("Config opt for {name} not in config_opts"))?;
|
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 {id} not in config_opts"))?;
|
||||||
|
|
||||||
// get the cached value
|
|
||||||
if let Some(val) = opt.val.clone() {
|
if let Some(val) = opt.val.clone() {
|
||||||
println!("get_config: {name} has cached value: {val}");
|
|
||||||
return Ok(val);
|
return Ok(val);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Evaluate the thunk
|
// in case it is not cached run the fun if it exists
|
||||||
let thunk = opt
|
if opt.fun.is_some() {
|
||||||
.thunk
|
let fun = opt.fun.take().unwrap();
|
||||||
.take()
|
drop(config_opts);
|
||||||
.ok_or_else(|| mize_err!("ConfigOpt {name} does not have a thunk"))?;
|
let res = fun()?;
|
||||||
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 mut config_opts = self.config_opts.lock().unwrap();
|
||||||
match config_opts.get_mut(name) {
|
let mut opt = config_opts.get_mut(&id).unwrap();
|
||||||
Some(opt) => {}
|
opt.fun = Some(fun);
|
||||||
None => {
|
opt.val = Some(res.clone());
|
||||||
let opt = ConfigOpt {
|
return Ok(res);
|
||||||
name: name.to_owned(),
|
|
||||||
val: None,
|
|
||||||
thunk: None,
|
|
||||||
};
|
|
||||||
config_opts.insert(name.to_string(), opt);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ConfigOptNameAndMize {
|
|
||||||
name: name.to_string(),
|
|
||||||
mize: self.clone(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run(&mut self) -> MizeResult<()> {
|
Err(mize_err!("ConfigOpt {id} has no fun and no cached value"))
|
||||||
let part_names = self.part_names();
|
|
||||||
for part_name in part_names {
|
|
||||||
let mut part = self.get_part(part_name)?;
|
|
||||||
//println!("running part {part_name}");
|
|
||||||
part.run(&mut self.clone())?;
|
|
||||||
}
|
}
|
||||||
let mut threads_inner = self.threads.lock().unwrap();
|
|
||||||
for (id, name, handle) in threads_inner.drain(..) {
|
pub fn dyn_call(&mut self, part_id: MizeId, method: &str, args: ItemData) -> MizeResult<ItemData> {
|
||||||
if let Some(handle) = handle {
|
debug!("dyn call {}/{} with args: {}", part_id, method, args);
|
||||||
handle.join().unwrap()?;
|
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 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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_part_native<T: MizePart + Send + Sync + 'static>(
|
pub fn get_part_gen<T: MizePart + Send + Sync + 'static>(
|
||||||
&mut self,
|
&mut self,
|
||||||
name: &str,
|
name: &str,
|
||||||
) -> MizeResult<MizePartGuard<T>> {
|
) -> MizeResult<T> {
|
||||||
let mut dyn_guard = self.get_part(name)?;
|
let dyn_part = self.get_part(name)?;
|
||||||
let part = dyn_guard.part.take().unwrap();
|
let concrete_part = dyn_part
|
||||||
drop(dyn_guard);
|
.into_any()
|
||||||
let concrete_part = part.into_any().downcast::<T>().unwrap();
|
.downcast::<T>()
|
||||||
Ok(MizePartGuard {
|
.map_err(|_| mize_err!("failed to downcast"))?;
|
||||||
mize: self.clone(),
|
Ok(*concrete_part)
|
||||||
part: Some(*concrete_part),
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
pub fn add_name_only_part(&mut self, name: &'static str) {
|
pub fn id_path(&self, id: &MizeId) -> MizeResult<String> {
|
||||||
let part = NameOnlyPart {
|
Ok(id.path().join("/"))
|
||||||
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
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
2
core/src/instance/msg_thread.rs
Normal file
2
core/src/instance/msg_thread.rs
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
// Message thread handling for the Mize instance.
|
||||||
|
// This module is reserved for future message threading functionality.
|
||||||
@@ -2,13 +2,13 @@ use core::fmt;
|
|||||||
use flume::Sender;
|
use flume::Sender;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use tracing::trace;
|
|
||||||
|
|
||||||
use crate::error::MizeResult;
|
use crate::error::MizeResult;
|
||||||
use crate::id::MizeId;
|
use crate::id::MizeId;
|
||||||
use crate::instance::connection::Connection;
|
use crate::instance::connection::Connection;
|
||||||
use crate::item::Item;
|
use crate::item::Item;
|
||||||
use crate::proto::MizeMessage;
|
use crate::proto::MizeMessage;
|
||||||
|
use crate::prelude::*;
|
||||||
|
|
||||||
use super::Mize;
|
use super::Mize;
|
||||||
|
|
||||||
@@ -1,5 +1,3 @@
|
|||||||
use tracing_subscriber::registry::Data;
|
|
||||||
|
|
||||||
use crate::item::IntoItemData;
|
use crate::item::IntoItemData;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
@@ -43,21 +41,21 @@ fn test_sort_keys() -> MizeResult<()> {
|
|||||||
fn test_get_module_hash() -> MizeResult<()> {
|
fn test_get_module_hash() -> MizeResult<()> {
|
||||||
let mut instance = Mize::empty()?;
|
let mut instance = Mize::empty()?;
|
||||||
|
|
||||||
use crate::platform::os::get_module_hash;
|
use crate::platform::os::get_part_hash;
|
||||||
let selector_toml = r#"
|
let selector_toml = r#"
|
||||||
mize_version = "0.0.0"
|
mize_version = "0.0.0"
|
||||||
system = "x86_64-unknown-linux-gnu"
|
system = "x86_64-unknown-linux-gnu"
|
||||||
toolchain_version = "rustc 1.78.0 (9b00956e5 2024-04-29)"
|
toolchain_version = "rustc 1.78.0 (9b00956e5 2024-04-29)"
|
||||||
"#;
|
"#;
|
||||||
|
|
||||||
instance.set_blocking("0/config/selector", ItemData::from_toml(&selector_toml)?)?;
|
// Pass selector fields as hash options (merges over defaults).
|
||||||
|
let options = ItemData::from_toml(&selector_toml)?;
|
||||||
|
let (hash, selector) = get_part_hash(&mut instance, "String", options)?;
|
||||||
|
|
||||||
let (hash, selector) = get_module_hash(&mut instance, "String", ItemData::new())?;
|
println!("hash: {}", hash);
|
||||||
|
println!("selector: {}", selector);
|
||||||
|
|
||||||
test_println!("hash: {}", hash);
|
assert_eq!(hash, "227b0216f9b7a324a621ff773c5241c9".to_owned());
|
||||||
test_println!("selector: {}", selector);
|
|
||||||
|
|
||||||
assert_eq!(hash, "e96edf60216688314efc263c1aa8d6ce".to_owned());
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -70,7 +68,7 @@ fn test_set_store_path() -> MizeResult<()> {
|
|||||||
|
|
||||||
let config = instance.get("0/config")?.as_data_full()?;
|
let config = instance.get("0/config")?.as_data_full()?;
|
||||||
|
|
||||||
test_println!("data at 0/config: {}", config);
|
println!("data at 0/config: {}", config);
|
||||||
|
|
||||||
instance.set_blocking("0/config/store_path", store_path.clone().into_item_data())?;
|
instance.set_blocking("0/config/store_path", store_path.clone().into_item_data())?;
|
||||||
|
|
||||||
@@ -252,12 +250,3 @@ fn test_item_merge() -> MizeResult<()> {
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
#[test]
|
|
||||||
#[should_panic(expected = "correct panic")]
|
|
||||||
fn test_cant_set_non_existent_item() -> () {
|
|
||||||
let instance = Instance::empty().expect("wrong panic");
|
|
||||||
instance.set_blocking("5", "hello world".into_item_data()).expect("correct panic");
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
use flume::Receiver;
|
use flume::Receiver;
|
||||||
use std::borrow::BorrowMut;
|
use std::borrow::BorrowMut;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use tracing::{error, trace, warn};
|
|
||||||
|
|
||||||
use crate::error::{MizeError, MizeResult, MizeResultTrait};
|
use crate::error::{MizeError, MizeResult, MizeResultTrait};
|
||||||
use crate::id::MizeId;
|
use crate::id::MizeId;
|
||||||
use crate::mize_err;
|
use crate::mize_err;
|
||||||
|
use crate::prelude::*;
|
||||||
use crate::proto::{MessageCmd, MizeMessage};
|
use crate::proto::{MessageCmd, MizeMessage};
|
||||||
use crate::{instance::Mize, item::ItemData};
|
use crate::{instance::Mize, item::ItemData};
|
||||||
|
|
||||||
@@ -215,6 +215,19 @@ fn handle_msg(msg: &mut MizeMessage, instance: &Mize) -> MizeResult<()> {
|
|||||||
}
|
}
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
MessageCmd::Custom(type_val) => {
|
||||||
|
let listeners = instance.listeners.lock()?;
|
||||||
|
let type_str = type_val.value_string()?;
|
||||||
|
let msg = msg.clone();
|
||||||
|
let mut mize = instance.clone();
|
||||||
|
println!("type_str: {}", type_str);
|
||||||
|
println!("listeners: {:?}", listeners.keys());
|
||||||
|
if let Some(listener) = listeners.get(&type_str) {
|
||||||
|
let listener = listener.clone();
|
||||||
|
drop(listeners);
|
||||||
|
listener(msg, &mut mize)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
_ => {
|
_ => {
|
||||||
return Err(mize_err!("got a message, that is not handeled"));
|
return Err(mize_err!("got a message, that is not handeled"));
|
||||||
}
|
}
|
||||||
1686
core/src/item.rs
Normal file
1686
core/src/item.rs
Normal file
File diff suppressed because it is too large
Load Diff
308
core/src/lib.rs
Normal file
308
core/src/lib.rs
Normal file
@@ -0,0 +1,308 @@
|
|||||||
|
#![allow(warnings)]
|
||||||
|
#![feature(str_as_str)]
|
||||||
|
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use std::fmt::format;
|
||||||
|
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! out {
|
||||||
|
($($arg:tt)*) => {
|
||||||
|
// format_args! handles string interpolation safely at compile time
|
||||||
|
if let Err(e) = $crate::write_to_mize_stdout(format_args!("{}\n", format_args!($($arg)*))) {
|
||||||
|
// Choose your error handling behavior here (panic, print to stderr, or ignore)
|
||||||
|
eprintln!("out! macro failed to write!!");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write_to_mize_stdout(args: std::fmt::Arguments) -> MizeResult<()> {
|
||||||
|
print!("{}", args);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Mize Shell (msh) macro - wraps command execution with nixpkgs executable resolution.
|
||||||
|
///
|
||||||
|
/// If the executable is given as a bare name (identifier), it is resolved via
|
||||||
|
/// [`msh_get_nix_bin`] to a nixpkgs store path. If it is given as a string literal
|
||||||
|
/// containing `/`, it is treated as a path and used as-is.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
/// ```ignore
|
||||||
|
/// msh!(git status)?; // resolves `git` from nixpkgs
|
||||||
|
/// msh!(curl https://example.com)?;
|
||||||
|
/// msh!("/usr/bin/git" status)?; // uses path directly
|
||||||
|
/// ```
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! msh {
|
||||||
|
// First token is a string literal — check if it's a path at runtime
|
||||||
|
($cmd:literal $($rest:tt)*) => {{
|
||||||
|
let cmd_str: &str = $cmd;
|
||||||
|
let resolved: String = if cmd_str.contains('/') {
|
||||||
|
cmd_str.to_string()
|
||||||
|
} else {
|
||||||
|
$crate::msh_get_nix_bin(cmd_str)
|
||||||
|
};
|
||||||
|
let mut cmd = ::std::process::Command::new(resolved);
|
||||||
|
$(cmd.arg(::std::stringify!($rest));)*
|
||||||
|
let status = cmd.status()?;
|
||||||
|
if status.success() {
|
||||||
|
::std::result::Result::Ok(())
|
||||||
|
} else {
|
||||||
|
::std::result::Result::Err(::std::io::Error::new(
|
||||||
|
::std::io::ErrorKind::Other,
|
||||||
|
::std::format!("msh: command exited with {}", status),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}};
|
||||||
|
// First token is an identifier — always resolve via nixpkgs
|
||||||
|
($cmd:ident $($rest:tt)*) => {{
|
||||||
|
let resolved: String = $crate::msh_get_nix_bin(::std::stringify!($cmd));
|
||||||
|
let mut cmd = ::std::process::Command::new(resolved);
|
||||||
|
$(cmd.arg(::std::stringify!($rest));)*
|
||||||
|
let status = cmd.status()?;
|
||||||
|
if status.success() {
|
||||||
|
::std::result::Result::Ok(())
|
||||||
|
} else {
|
||||||
|
::std::result::Result::Err(::std::io::Error::new(
|
||||||
|
::std::io::ErrorKind::Other,
|
||||||
|
::std::format!("msh: command exited with {}", status),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub mod prelude {
|
||||||
|
pub use super::*;
|
||||||
|
pub use instance::Mize;
|
||||||
|
pub use id::*;
|
||||||
|
pub use error::MizeError;
|
||||||
|
pub use crate::mize_err;
|
||||||
|
pub use error::MizeResult;
|
||||||
|
pub use error::IntoMizeResult;
|
||||||
|
pub use item::{Item, ItemData, IntoItemData};
|
||||||
|
pub use config::*;
|
||||||
|
pub use api::*;
|
||||||
|
pub use mize_macros::*;
|
||||||
|
pub use crate::tracing::{trace, warn, debug, info, error};
|
||||||
|
}
|
||||||
|
pub use flume;
|
||||||
|
use tokio::runtime::Builder;
|
||||||
|
use tokio::runtime::Runtime;
|
||||||
|
pub use tracing;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// the core part has the code that can run on any platform
|
||||||
|
pub mod config;
|
||||||
|
pub mod error;
|
||||||
|
pub mod id;
|
||||||
|
pub mod instance;
|
||||||
|
pub mod item;
|
||||||
|
pub mod memstore;
|
||||||
|
pub mod proto;
|
||||||
|
pub mod types;
|
||||||
|
pub mod c_api;
|
||||||
|
pub mod api;
|
||||||
|
|
||||||
|
pub use async_trait::async_trait;
|
||||||
|
pub use ciborium;
|
||||||
|
pub use error::MizeError;
|
||||||
|
pub use error::MizeResult;
|
||||||
|
pub use instance::Mize;
|
||||||
|
pub use instance::{
|
||||||
|
MizePart, PartMetadata, OptMetadata, MethodMetadata, NativeMetadata, parse_method_signature,
|
||||||
|
};
|
||||||
|
pub use mize_macros::*;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
use std::sync::{Arc, Mutex, OnceLock};
|
||||||
|
|
||||||
|
use crate::platform::os;
|
||||||
|
|
||||||
|
/// Global Mize instance storage.
|
||||||
|
/// This is set once when the Mize instance is created and can be accessed from anywhere.
|
||||||
|
pub static GLOBAL_MIZE: OnceLock<Arc<Mutex<Mize>>> = OnceLock::new();
|
||||||
|
|
||||||
|
/// Get the global Mize instance.
|
||||||
|
/// Returns a clone of the Mize instance (which is cheap as it only clones Arcs internally).
|
||||||
|
pub fn get_global_mize() -> MizeResult<Mize> {
|
||||||
|
let arc = GLOBAL_MIZE
|
||||||
|
.get()
|
||||||
|
.ok_or_else(|| mize_err!("No global Mize instance set"))?;
|
||||||
|
let mize = arc
|
||||||
|
.lock()
|
||||||
|
.map_err(|_| mize_err!("Failed to lock global Mize"))?;
|
||||||
|
Ok(mize.clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the global Mize instance.
|
||||||
|
pub fn set_global_mize(mize: Mize) -> MizeResult<()> {
|
||||||
|
let arc = Arc::new(Mutex::new(mize.clone()));
|
||||||
|
if let Err(_) = GLOBAL_MIZE.set(arc) {
|
||||||
|
let a = GLOBAL_MIZE.get().unwrap();
|
||||||
|
let mut b = a.lock().map_err(|_| mize_err!("Failed to lock global Mize"))?;
|
||||||
|
*b = mize;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// C API function to set the global Mize instance from a dynamically loaded part.
|
||||||
|
/// This is a hack to allow Rust parts loaded as .so files to access the global Mize instance.
|
||||||
|
// i hate rust.
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub extern "C" fn mize_rust_hack_set_global(mize: Arc<Mutex<Mize>>, dispatch_ptr: *const std::ffi::c_void) {
|
||||||
|
// and also set the tokio runtime
|
||||||
|
//let mize_locked = mize.lock().unwrap();
|
||||||
|
//let rt = mize_locked.runtime.lock().unwrap();
|
||||||
|
//rt.enter();
|
||||||
|
//drop(rt);
|
||||||
|
//drop(mize_locked);
|
||||||
|
let rt = Builder::new_multi_thread().enable_all().build().unwrap();
|
||||||
|
let guard = rt.enter();
|
||||||
|
std::mem::forget(guard);
|
||||||
|
|
||||||
|
use crate::tracing::Dispatch;
|
||||||
|
let _ = GLOBAL_MIZE.set(mize);
|
||||||
|
|
||||||
|
//and also init tracing
|
||||||
|
unsafe {
|
||||||
|
/// i hate rust.
|
||||||
|
let dispatch = &*(dispatch_ptr as *const Dispatch);
|
||||||
|
if let Err(e) = tracing::dispatcher::set_global_default(dispatch.clone()) {
|
||||||
|
println!("failed to set tracing dispatcher: {e}");
|
||||||
|
}
|
||||||
|
tracing::callsite::rebuild_interest_cache();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Resolve an executable name to its full path in the nixpkgs store.
|
||||||
|
///
|
||||||
|
/// Reads `nix.pkgs.ref` (nixpkgs commit hash) and `nix.bin` (path to the `nix`
|
||||||
|
/// binary) from the Mize config, then runs:
|
||||||
|
///
|
||||||
|
/// ```text
|
||||||
|
/// <nix.bin> eval --raw nixpkgs/<hash>#<pkg>
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// and returns `<outPath>/bin/<executable>`.
|
||||||
|
///
|
||||||
|
/// Certain executable names are mapped to different nixpkgs attribute names
|
||||||
|
/// via a hardcoded match (e.g. `npm` → `nodejs`).
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
/// Panics if the global Mize instance is not set, the config values are missing,
|
||||||
|
/// or `nix eval` fails.
|
||||||
|
pub fn msh_get_nix_bin(name: &str) -> String {
|
||||||
|
// Hardcoded mapping of executable names to nixpkgs attribute names
|
||||||
|
let pkg = match name {
|
||||||
|
"npm" => "nodejs",
|
||||||
|
"node" => "nodejs",
|
||||||
|
"yarn" => "yarn",
|
||||||
|
"pnpm" => "nodePackages.pnpm",
|
||||||
|
_ => name,
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut mize = get_global_mize().expect("msh_get_nix_bin: no global Mize instance set");
|
||||||
|
|
||||||
|
let nixpkgs_ref = mize
|
||||||
|
.get_config("nix.pkgs.ref")
|
||||||
|
.expect("msh_get_nix_bin: failed to get config nix.pkgs.ref")
|
||||||
|
.value_string()
|
||||||
|
.expect("msh_get_nix_bin: nix.pkgs.ref is not a string");
|
||||||
|
|
||||||
|
let nix_bin = mize
|
||||||
|
.get_config("nix.bin")
|
||||||
|
.expect("msh_get_nix_bin: failed to get config nix.bin")
|
||||||
|
.value_string()
|
||||||
|
.expect("msh_get_nix_bin: nix.bin is not a string");
|
||||||
|
|
||||||
|
// Run: nix eval --raw nixpkgs/<hash>#<pkg>
|
||||||
|
// --raw outputs just the store path without quotes
|
||||||
|
let attr = format!("nixpkgs/{}#{}", nixpkgs_ref, pkg);
|
||||||
|
let output = std::process::Command::new(&nix_bin)
|
||||||
|
.args(["eval", "--raw", &attr])
|
||||||
|
.output()
|
||||||
|
.expect("msh_get_nix_bin: failed to spawn nix eval");
|
||||||
|
|
||||||
|
if !output.status.success() {
|
||||||
|
panic!(
|
||||||
|
"msh_get_nix_bin: nix eval failed for {}: {}",
|
||||||
|
attr,
|
||||||
|
String::from_utf8_lossy(&output.stderr)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let out_path = String::from_utf8(output.stdout)
|
||||||
|
.expect("msh_get_nix_bin: nix eval output is not valid UTF-8")
|
||||||
|
.trim()
|
||||||
|
.to_string();
|
||||||
|
|
||||||
|
format!("{}/bin/{}", out_path, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Run a command with Mize Shell executable resolution.
|
||||||
|
///
|
||||||
|
/// This is the function-based equivalent of the [`msh!`] macro, with proper
|
||||||
|
/// argument handling (no `stringify!` limitations).
|
||||||
|
///
|
||||||
|
/// If `cmd` contains `/` it is treated as a path and used as-is.
|
||||||
|
/// Otherwise it is resolved via [`msh_get_nix_bin`].
|
||||||
|
///
|
||||||
|
/// Returns `Ok(())` if the command exits successfully, or an `io::Error` otherwise.
|
||||||
|
pub fn msh_run(cmd: &str, args: &[&str]) -> std::io::Result<()> {
|
||||||
|
let resolved = if cmd.contains('/') {
|
||||||
|
cmd.to_string()
|
||||||
|
} else {
|
||||||
|
msh_get_nix_bin(cmd)
|
||||||
|
};
|
||||||
|
let mut command = std::process::Command::new(resolved);
|
||||||
|
command.args(args);
|
||||||
|
let status = command.status()?;
|
||||||
|
if status.success() {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(std::io::Error::new(
|
||||||
|
std::io::ErrorKind::Other,
|
||||||
|
format!("msh: command exited with {}", status),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// platform specific stuff
|
||||||
|
pub mod platform {
|
||||||
|
use crate::{Mize, MizeResult};
|
||||||
|
|
||||||
|
#[cfg(feature = "target-js")]
|
||||||
|
pub mod js;
|
||||||
|
|
||||||
|
#[cfg(feature = "target-os")]
|
||||||
|
pub mod os;
|
||||||
|
|
||||||
|
pub fn init(mize: &mut Mize) -> MizeResult<()> {
|
||||||
|
#[cfg(feature = "target-os")] {
|
||||||
|
os::os_instance_init(mize)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "target-js")] {
|
||||||
|
js::wasm_instance_init(mize)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn load_part(part_name: &str, path: Option<String>, at_id: Option<&str>) -> MizeResult<()> {
|
||||||
|
#[cfg(feature = "target-os")] {
|
||||||
|
os::load_part(part_name, path, at_id)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
pub fn fetch_part(mize: &mut Mize, part_name: &str) -> MizeResult<String> {
|
||||||
|
#[cfg(feature = "target-os")] {
|
||||||
|
os::fetch_part(mize, part_name)?;
|
||||||
|
}
|
||||||
|
Ok("alsdkjf".to_owned())
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,7 +4,7 @@ use std::collections::binary_heap::Iter;
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::iter::Map;
|
use std::iter::Map;
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
use tracing::{instrument, trace};
|
use crate::tracing::instrument;
|
||||||
|
|
||||||
use crate::error::{IntoMizeResult, MizeError, MizeResult};
|
use crate::error::{IntoMizeResult, MizeError, MizeResult};
|
||||||
use crate::id::MizeId;
|
use crate::id::MizeId;
|
||||||
@@ -13,6 +13,7 @@ use crate::instance::Mize;
|
|||||||
use crate::item::get_raw_from_cbor;
|
use crate::item::get_raw_from_cbor;
|
||||||
use crate::item::{Item, ItemData};
|
use crate::item::{Item, ItemData};
|
||||||
use crate::mize_err;
|
use crate::mize_err;
|
||||||
|
use crate::prelude::*;
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct MemStore {
|
pub struct MemStore {
|
||||||
@@ -145,7 +146,7 @@ fn id_to_u64(id: MizeId) -> MizeResult<u64> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn str_to_u64(id_str: &str) -> MizeResult<u64> {
|
fn str_to_u64(id_str: &str) -> MizeResult<u64> {
|
||||||
id_str.parse().mize_result_msg(format!(
|
id_str.parse().mres_msg(format!(
|
||||||
"Could not parse the store_part of mizeid {} into a u64 for the MemStore",
|
"Could not parse the store_part of mizeid {} into a u64 for the MemStore",
|
||||||
id_str
|
id_str
|
||||||
))
|
))
|
||||||
@@ -11,10 +11,9 @@ use web_sys::{WorkerOptions, WorkerType};
|
|||||||
|
|
||||||
use crate::core::item::IntoItemData;
|
use crate::core::item::IntoItemData;
|
||||||
use crate::error::MizeResult;
|
use crate::error::MizeResult;
|
||||||
use crate::instance::module::EmptyModule;
|
|
||||||
use crate::Mize;
|
use crate::Mize;
|
||||||
use crate::MizeError;
|
use crate::MizeError;
|
||||||
use crate::{mize_err, Module};
|
use crate::mize_err;
|
||||||
|
|
||||||
// console_log macro
|
// console_log macro
|
||||||
// that can be copied into other files for debugging purposes
|
// that can be copied into other files for debugging purposes
|
||||||
@@ -49,17 +48,72 @@ static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;
|
|||||||
pub fn wasm_instance_init(mize: &mut Mize) -> MizeResult<()> {
|
pub fn wasm_instance_init(mize: &mut Mize) -> MizeResult<()> {
|
||||||
console_log!("Hello world from wasm_instance_init!!!!!!!!!!");
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "target-obsidian")]
|
||||||
|
pub fn obsidian_get_env_var(name: &str) -> Option<String> {
|
||||||
|
use js_sys::{Reflect, Object};
|
||||||
|
// 1. Get the global `process` object
|
||||||
|
let global = js_sys::global();
|
||||||
|
let process = Reflect::get(&global, &JsValue::from_str("process")).ok()?;
|
||||||
|
|
||||||
|
if process.is_undefined() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Get the `env` object from `process`
|
||||||
|
let env = Reflect::get(&process, &JsValue::from_str("env")).ok()?;
|
||||||
|
if env.is_undefined() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Get the `MIZE_STORE` value from `env`
|
||||||
|
let mize_store = Reflect::get(&env, &JsValue::from_str("MIZE_STORE")).ok()?;
|
||||||
|
|
||||||
|
// 4. Convert the JsValue to a Rust String if it exists
|
||||||
|
mize_store.as_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
pub fn load_module(mize: &mut Mize, name: &str, path: Option<String>) -> MizeResult<()> {
|
pub fn load_module(mize: &mut Mize, name: &str, path: Option<String>) -> MizeResult<()> {
|
||||||
console_log!("loading module: {}", name);
|
console_log!("loading module NOT IMPLEMENTED: {}", name);
|
||||||
|
|
||||||
wasm_bindgen_futures::spawn_local(load_module_async(mize.clone(), name.to_owned(), path));
|
//wasm_bindgen_futures::spawn_local(load_module_async(mize.clone(), name.to_owned(), path));
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
async fn load_module_async(mut mize: Mize, name: String, path: Option<String>) -> () {
|
async fn load_module_async(mut mize: Mize, name: String, path: Option<String>) -> () {
|
||||||
let name = name.as_str();
|
let name = name.as_str();
|
||||||
let module_url = fetch_module(&mut mize, name).unwrap();
|
let module_url = fetch_module(&mut mize, name).unwrap();
|
||||||
@@ -145,8 +199,9 @@ async fn load_module_async(mut mize: Mize, name: String, path: Option<String>) -
|
|||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
pub fn fetch_module(mize: &mut Mize, name: &str) -> MizeResult<String> {
|
pub fn fetch_module(mize: &mut Mize, name: &str) -> MizeResult<String> {
|
||||||
let module_name_with_slashes = name.replace(".", "/");
|
let module_name_with_slashes = name.replace(".", "/");
|
||||||
if let Ok(module_path) = mize
|
if let Ok(module_path) = mize
|
||||||
.get(format!(
|
.get(format!(
|
||||||
@@ -191,6 +246,7 @@ async fn download_module(mize: &mut Mize, name: &str) -> MizeResult<js_sys::Arra
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[wasm_bindgen]
|
#[wasm_bindgen]
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct JsMize {
|
pub struct JsMize {
|
||||||
inner: NonNull<Mize>,
|
inner: NonNull<Mize>,
|
||||||
}
|
}
|
||||||
@@ -207,10 +263,7 @@ pub async fn new_js_instance(config_json_str: String) -> JsMize {
|
|||||||
|
|
||||||
let config = ItemData::from_json(config_json_str).expect("parsing of json config failed");
|
let config = ItemData::from_json(config_json_str).expect("parsing of json config failed");
|
||||||
|
|
||||||
let mut mize = Mize::empty().expect("Mize::empty() failed");
|
let mut mize = Mize::with_config(config).expect("Mize::new() failed");
|
||||||
|
|
||||||
mize.set_blocking("0/config", config)
|
|
||||||
.expect("Failed to set the config at item 0");
|
|
||||||
|
|
||||||
let mut js_instance = JsMize {
|
let mut js_instance = JsMize {
|
||||||
inner: NonNull::from(Box::leak(Box::new(mize))),
|
inner: NonNull::from(Box::leak(Box::new(mize))),
|
||||||
@@ -4,17 +4,19 @@ use std::fs::OpenOptions;
|
|||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use sysinfo::{Pid, ProcessRefreshKind, RefreshKind, System};
|
use sysinfo::{Pid, ProcessRefreshKind, RefreshKind, System};
|
||||||
use tracing::debug;
|
|
||||||
|
|
||||||
use crate::core::id::MizeId;
|
use crate::id::MizeId;
|
||||||
use crate::core::item::{Item, ItemData};
|
use crate::item::{Item, ItemData};
|
||||||
use crate::error::{IntoMizeResult, MizeError, MizeResult};
|
use crate::error::{IntoMizeResult, MizeError, MizeResult};
|
||||||
use crate::instance::store::IdIter;
|
use crate::instance::store::IdIter;
|
||||||
use crate::instance::{self, Mize};
|
use crate::instance::{self, Mize};
|
||||||
use crate::item::get_raw_from_cbor;
|
use crate::item::get_raw_from_cbor;
|
||||||
use crate::item::IntoItemData;
|
use crate::item::IntoItemData;
|
||||||
use crate::memstore::MemStore;
|
use crate::memstore::MemStore;
|
||||||
use crate::{core::instance::store::Store, mize_err};
|
use crate::{instance::store::Store, mize_err};
|
||||||
|
use crate::prelude::*;
|
||||||
|
|
||||||
|
#[cfg(target_family = "unix")]
|
||||||
use tokio::net::UnixStream;
|
use tokio::net::UnixStream;
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
@@ -72,6 +74,7 @@ impl FileStore {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(target_family = "unix")]
|
||||||
async fn try_connect_to_sock(store_path: String) -> MizeResult<()> {
|
async fn try_connect_to_sock(store_path: String) -> MizeResult<()> {
|
||||||
match UnixStream::connect(PathBuf::from(store_path).join("sock")).await {
|
match UnixStream::connect(PathBuf::from(store_path).join("sock")).await {
|
||||||
Err(_) => Err(mize_err!("")),
|
Err(_) => Err(mize_err!("")),
|
||||||
@@ -79,15 +82,30 @@ async fn try_connect_to_sock(store_path: String) -> MizeResult<()> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(target_family = "windows")]
|
||||||
|
async fn try_connect_to_sock(store_path: String) -> MizeResult<()> {
|
||||||
|
use tokio::net::windows::named_pipe::{ClientOptions};
|
||||||
|
let pipe_name = format!(
|
||||||
|
"\\.\\pipe\\mize_{}",
|
||||||
|
store_path.replace('\\', "_").replace('/', "_").replace(':', "_")
|
||||||
|
);
|
||||||
|
// ClientOptions::new().open() is synchronous, not async
|
||||||
|
// So we just try to open and immediately close it
|
||||||
|
match ClientOptions::new().open(&pipe_name) {
|
||||||
|
Err(_) => Err(mize_err!("")),
|
||||||
|
Ok(_) => Ok(()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Store for FileStore {
|
impl Store for FileStore {
|
||||||
fn new_id(&self) -> MizeResult<String> {
|
fn new_id(&self) -> MizeResult<String> {
|
||||||
let mut next_id: u64 =
|
let mut next_id: u64 =
|
||||||
String::from_utf8(fs::read(self.path.join("next_id")).mize_result_msg(format!(
|
String::from_utf8(fs::read(self.path.join("next_id")).mres_msg(format!(
|
||||||
"could not read next_id at '{}'",
|
"could not read next_id at '{}'",
|
||||||
self.path.display()
|
self.path.display()
|
||||||
))?)?
|
))?)?
|
||||||
.parse()
|
.parse()
|
||||||
.mize_result_msg(format!(
|
.mres_msg(format!(
|
||||||
"could not parse next_id at '{}' to u64",
|
"could not parse next_id at '{}' to u64",
|
||||||
self.path.display()
|
self.path.display()
|
||||||
))?;
|
))?;
|
||||||
@@ -132,7 +150,7 @@ impl Store for FileStore {
|
|||||||
|
|
||||||
let file = OpenOptions::new().read(true).create(true).open(path)?;
|
let file = OpenOptions::new().read(true).create(true).open(path)?;
|
||||||
|
|
||||||
let cbor_val: CborValue = ciborium::from_reader(file).mize_result_msg(format!(
|
let cbor_val: CborValue = ciborium::from_reader(file).mres_msg(format!(
|
||||||
"could not read file '{}' from FileStore",
|
"could not read file '{}' from FileStore",
|
||||||
self.path.join("store").join(id.store_part()).display()
|
self.path.join("store").join(id.store_part()).display()
|
||||||
))?;
|
))?;
|
||||||
@@ -163,17 +181,20 @@ impl Store for FileStore {
|
|||||||
)
|
)
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let cbor_value: CborValue = ciborium::from_reader(file).mize_result_msg(format!(
|
let cbor_value: CborValue = ciborium::from_reader(file).mres_msg(format!(
|
||||||
"could not read file '{}' from FileStore",
|
"could not read file '{}' from FileStore",
|
||||||
self.path.join("store").join(id.store_part()).display()
|
self.path.join("store").join(id.store_part()).display()
|
||||||
))?;
|
))?;
|
||||||
let data: ItemData = cbor_value.into_item_data();
|
let data: ItemData = cbor_value.into_item_data();
|
||||||
|
|
||||||
let tmp = id.path();
|
let tmp = id.path();
|
||||||
let mut path_iter = tmp.into_iter();
|
let mut path_iter = tmp.iter();
|
||||||
path_iter.next();
|
path_iter.next();
|
||||||
let new_path: Vec<String> = path_iter.map(|v| v.to_owned()).collect();
|
let new_path: Vec<String> = path_iter.map(|v| v.to_owned()).collect();
|
||||||
|
//println!("data: {}", data);
|
||||||
|
//println!("new_path: {:?}", new_path);
|
||||||
let ret_data = data.get_path(new_path)?;
|
let ret_data = data.get_path(new_path)?;
|
||||||
|
//println!("ret_data: {}", ret_data);
|
||||||
|
|
||||||
return Ok(ret_data);
|
return Ok(ret_data);
|
||||||
}
|
}
|
||||||
@@ -188,6 +209,7 @@ impl Store for FileStore {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(target_family = "unix")]
|
||||||
fn valid_pid_file(path: &Path) -> MizeResult<Option<u32>> {
|
fn valid_pid_file(path: &Path) -> MizeResult<Option<u32>> {
|
||||||
let pid_file_path = path.join("pid");
|
let pid_file_path = path.join("pid");
|
||||||
|
|
||||||
@@ -197,12 +219,12 @@ fn valid_pid_file(path: &Path) -> MizeResult<Option<u32>> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// read pid file
|
// read pid file
|
||||||
let pid: u32 = String::from_utf8(fs::read(&pid_file_path).mize_result_msg(format!(
|
let pid: u32 = String::from_utf8(fs::read(&pid_file_path).mres_msg(format!(
|
||||||
"Could not read contents of pid file at '{}'",
|
"Could not read contents of pid file at '{}'",
|
||||||
pid_file_path.display()
|
pid_file_path.display()
|
||||||
))?)?
|
))?)?
|
||||||
.parse()
|
.parse()
|
||||||
.mize_result_msg(format!(
|
.mres_msg(format!(
|
||||||
"Could not parse contents of pid file at '{}' to u32",
|
"Could not parse contents of pid file at '{}' to u32",
|
||||||
pid_file_path.display()
|
pid_file_path.display()
|
||||||
))?;
|
))?;
|
||||||
414
core/src/platform/os/logging.rs
Normal file
414
core/src/platform/os/logging.rs
Normal file
@@ -0,0 +1,414 @@
|
|||||||
|
use clap::{crate_version, Arg, Command};
|
||||||
|
use clap::{ArgAction, ArgMatches};
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
use colored::Colorize;
|
||||||
|
use core::fmt;
|
||||||
|
use std::env;
|
||||||
|
use std::fmt::Write;
|
||||||
|
use std::sync::atomic::AtomicUsize;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use std::sync::Mutex;
|
||||||
|
use tokio::sync::mpsc;
|
||||||
|
use tracing::level_filters::LevelFilter;
|
||||||
|
use tracing::{Dispatch, Event};
|
||||||
|
use tracing::{field::Visit, Id, Subscriber};
|
||||||
|
use tracing::{span_enabled, Instrument, Level};
|
||||||
|
use tracing_core::Field;
|
||||||
|
use tracing_subscriber::filter::EnvFilter;
|
||||||
|
use tracing_subscriber::fmt::format;
|
||||||
|
use tracing_subscriber::fmt::FmtContext;
|
||||||
|
use tracing_subscriber::fmt::FormatEvent;
|
||||||
|
use tracing_subscriber::fmt::FormatFields;
|
||||||
|
use tracing_subscriber::fmt::FormattedFields;
|
||||||
|
use tracing_subscriber::registry::LookupSpan;
|
||||||
|
|
||||||
|
use crate::id::MizeId;
|
||||||
|
use crate::item::ItemData;
|
||||||
|
use crate::MizeError;
|
||||||
|
use crate::MizeResult;
|
||||||
|
use crate::prelude::*;
|
||||||
|
|
||||||
|
static DEFAULT_LOG_LEVEL: LevelFilter = LevelFilter::WARN;
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! trace {
|
||||||
|
($($arg:tt)*) => {
|
||||||
|
// format_args! handles string interpolation safely at compile time
|
||||||
|
if let Err(e) = $crate::write_to_mize_stdout(format_args!("[ {} ] {}\n", colored::Colorize::yellow("TRACE"), format_args!($($arg)*))) {
|
||||||
|
// Choose your error handling behavior here (panic, print to stderr, or ignore)
|
||||||
|
eprintln!("out! macro failed to write!!");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! debug {
|
||||||
|
($($arg:tt)*) => {
|
||||||
|
// format_args! handles string interpolation safely at compile time
|
||||||
|
if let Err(e) = $crate::write_to_mize_stdout(format_args!("[ {} ] {}\n", colored::Colorize::green("DEBUG"), format_args!($($arg)*))) {
|
||||||
|
// Choose your error handling behavior here (panic, print to stderr, or ignore)
|
||||||
|
eprintln!("out! macro failed to write!!");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! info {
|
||||||
|
($($arg:tt)*) => {
|
||||||
|
// format_args! handles string interpolation safely at compile time
|
||||||
|
if let Err(e) = $crate::write_to_mize_stdout(format_args!("[ {} ] {}\n", colored::Colorize::blue("INFO"), format_args!($($arg)*))) {
|
||||||
|
// Choose your error handling behavior here (panic, print to stderr, or ignore)
|
||||||
|
eprintln!("out! macro failed to write!!");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! warn {
|
||||||
|
($($arg:tt)*) => {
|
||||||
|
// format_args! handles string interpolation safely at compile time
|
||||||
|
if let Err(e) = $crate::write_to_mize_stdout(format_args!("[ {} ] {}\n", colored::Colorize::truecolor("WARN", 245, 164, 66), format_args!($($arg)*))) {
|
||||||
|
// Choose your error handling behavior here (panic, print to stderr, or ignore)
|
||||||
|
eprintln!("out! macro failed to write!!");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! error {
|
||||||
|
($($arg:tt)*) => {
|
||||||
|
// format_args! handles string interpolation safely at compile time
|
||||||
|
if let Err(e) = $crate::write_to_mize_stdout(format_args!("[ {} ] {}\n", colored::Colorize::red("ERROR"), format_args!($($arg)*))) {
|
||||||
|
// Choose your error handling behavior here (panic, print to stderr, or ignore)
|
||||||
|
eprintln!("out! macro failed to write!!");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
pub fn init_logger(cli_matches: Option<&ArgMatches>) {
|
||||||
|
trace!("init_logger called");
|
||||||
|
// to save log messages, before the logger is setup
|
||||||
|
let mut log_messages: Vec<(Level, String)> = Vec::new();
|
||||||
|
|
||||||
|
log_messages.push((Level::INFO, "Starting Mize".to_owned()));
|
||||||
|
|
||||||
|
// builder with DEFAULT_LOG_LEVEL
|
||||||
|
let mut builder = tracing_subscriber::fmt::Subscriber::builder();
|
||||||
|
|
||||||
|
// set loglevel
|
||||||
|
let mut log_level = DEFAULT_LOG_LEVEL;
|
||||||
|
log_messages.push((
|
||||||
|
Level::TRACE,
|
||||||
|
format!(
|
||||||
|
"set log level from DEFAULT_LOG_LEVEL to {}",
|
||||||
|
DEFAULT_LOG_LEVEL
|
||||||
|
),
|
||||||
|
));
|
||||||
|
|
||||||
|
// check env RUST_LOG
|
||||||
|
let mut val_rust_log = level_from_env("RUST_LOG", log_level);
|
||||||
|
if let Some(level_rust_log) = val_rust_log.0 {
|
||||||
|
log_level = level_rust_log;
|
||||||
|
log_messages.push((
|
||||||
|
Level::TRACE,
|
||||||
|
format!(
|
||||||
|
"set log level from the Variable RUST_LOG to {}",
|
||||||
|
level_rust_log
|
||||||
|
),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
log_messages.append(&mut val_rust_log.1);
|
||||||
|
|
||||||
|
// check env LOG
|
||||||
|
let mut val_mize_log = level_from_env("LOG", log_level);
|
||||||
|
if let Some(level_mize_log) = val_mize_log.0 {
|
||||||
|
debug!("set log level from the Variable LOG to {}", level_mize_log);
|
||||||
|
log_level = level_mize_log;
|
||||||
|
log_messages.push((
|
||||||
|
Level::TRACE,
|
||||||
|
format!(
|
||||||
|
"set log level from the Variable MIZE_LOG to {}",
|
||||||
|
level_mize_log
|
||||||
|
),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
log_messages.append(&mut val_mize_log.1);
|
||||||
|
|
||||||
|
// check env MIZE_LOG
|
||||||
|
let mut val_mize_log = level_from_env("MIZE_LOG", log_level);
|
||||||
|
if let Some(level_mize_log) = val_mize_log.0 {
|
||||||
|
log_level = level_mize_log;
|
||||||
|
log_messages.push((
|
||||||
|
Level::TRACE,
|
||||||
|
format!(
|
||||||
|
"set log level from the Variable MIZE_LOG to {}",
|
||||||
|
level_mize_log
|
||||||
|
),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
log_messages.append(&mut val_mize_log.1);
|
||||||
|
|
||||||
|
// check -vvvvv flags
|
||||||
|
if let Some(cli_matches) = cli_matches {
|
||||||
|
let v_flag_count = cli_matches.get_count("verbose");
|
||||||
|
if v_flag_count > 0 {
|
||||||
|
let default_log_level_as_num = match DEFAULT_LOG_LEVEL {
|
||||||
|
LevelFilter::OFF => 0,
|
||||||
|
LevelFilter::ERROR => 1,
|
||||||
|
LevelFilter::WARN => 2,
|
||||||
|
LevelFilter::INFO => 3,
|
||||||
|
LevelFilter::DEBUG => 4,
|
||||||
|
LevelFilter::TRACE => 5,
|
||||||
|
};
|
||||||
|
let new_log_level_num = default_log_level_as_num + v_flag_count;
|
||||||
|
log_level = match new_log_level_num {
|
||||||
|
0 => LevelFilter::OFF,
|
||||||
|
1 => LevelFilter::ERROR,
|
||||||
|
2 => LevelFilter::WARN,
|
||||||
|
3 => LevelFilter::INFO,
|
||||||
|
4 => LevelFilter::DEBUG,
|
||||||
|
5 => LevelFilter::TRACE,
|
||||||
|
_ => {
|
||||||
|
log_messages.push((
|
||||||
|
Level::TRACE,
|
||||||
|
format!(
|
||||||
|
"the number of verbose (-v) flags passed ({}) exeeds the maximumg log_level",
|
||||||
|
v_flag_count
|
||||||
|
),
|
||||||
|
));
|
||||||
|
LevelFilter::TRACE
|
||||||
|
}
|
||||||
|
};
|
||||||
|
log_messages.push((
|
||||||
|
Level::TRACE,
|
||||||
|
format!("set log level from the verbose flags to {}", log_level),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
// check --log-level arg
|
||||||
|
if let Some(log_level_arg_string) = cli_matches.get_one::<String>("log-level") {
|
||||||
|
let log_level_from_arg = match log_level_arg_string.to_owned().to_lowercase().as_str() {
|
||||||
|
"none" => LevelFilter::OFF,
|
||||||
|
"trace" => LevelFilter::TRACE,
|
||||||
|
"debug" => LevelFilter::DEBUG,
|
||||||
|
"info" => LevelFilter::INFO,
|
||||||
|
"warn" => LevelFilter::WARN,
|
||||||
|
"error" => LevelFilter::ERROR,
|
||||||
|
_ => {
|
||||||
|
log_messages.push((Level::WARN, format!(
|
||||||
|
"The argument to --log-level \"{}\" is not a valid log-level. loglevel stays at: {}"
|
||||||
|
, log_level_arg_string
|
||||||
|
, log_level
|
||||||
|
)));
|
||||||
|
log_level
|
||||||
|
}
|
||||||
|
};
|
||||||
|
log_level = log_level_from_arg;
|
||||||
|
log_messages.push((
|
||||||
|
Level::TRACE,
|
||||||
|
format!(
|
||||||
|
"set log level from the --log-level arg to {}",
|
||||||
|
log_level_from_arg
|
||||||
|
),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// actually set the log_level on the builder
|
||||||
|
log_messages.push((Level::INFO, format!("loglevel is now {}", log_level)));
|
||||||
|
|
||||||
|
// filter loging
|
||||||
|
//let mut my_env_filter = tracing_subscriber::filter::EnvFilter::new("")
|
||||||
|
//.add_directive("[get_raw_from_cbor]=debug".parse().expect("error parsing log directive"))
|
||||||
|
//.add_directive("[MemStore::get_value_raw]=trace".parse().expect("error parsing log directive"))
|
||||||
|
//.add_directive("trace".parse().unwrap())
|
||||||
|
//.add_directive("[get_raw_from_cbor]=debug".parse().expect("error parsing log directive"))
|
||||||
|
//;
|
||||||
|
|
||||||
|
//MinimalTracer::register();
|
||||||
|
|
||||||
|
debug!("log_level: {}", log_level);
|
||||||
|
let field_formatter = tracing_subscriber::fmt::format::debug_fn(|writer, field, value| {
|
||||||
|
if field.name() == "message" {
|
||||||
|
write!(writer, "{:?}", value)?;
|
||||||
|
} else {
|
||||||
|
write!(writer, "{}={:?}", field, value)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
});
|
||||||
|
let subscriber = builder
|
||||||
|
//.with_env_filter(my_env_filter)
|
||||||
|
.without_time()
|
||||||
|
.event_format(MyFormatter)
|
||||||
|
.fmt_fields(field_formatter)
|
||||||
|
.with_max_level(log_level)
|
||||||
|
.finish();
|
||||||
|
|
||||||
|
let dispatch = Dispatch::new(subscriber);
|
||||||
|
if let Err(e) = tracing::dispatcher::set_global_default(dispatch) {
|
||||||
|
println!("faild to set_global_default(dispatch): {e}");
|
||||||
|
}
|
||||||
|
|
||||||
|
// write out all log messages from log_messages
|
||||||
|
for (level, msg) in log_messages {
|
||||||
|
match level {
|
||||||
|
Level::ERROR => error!("{}", msg),
|
||||||
|
Level::WARN => warn!("{}", msg),
|
||||||
|
Level::INFO => info!("{}", msg),
|
||||||
|
Level::DEBUG => debug!("{}", msg),
|
||||||
|
Level::TRACE => trace!("{}", msg),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn level_from_env(
|
||||||
|
var_name: &str,
|
||||||
|
log_level: LevelFilter,
|
||||||
|
) -> (Option<LevelFilter>, Vec<(Level, String)>) {
|
||||||
|
let mut log_messages: Vec<(Level, String)> = Vec::new();
|
||||||
|
let var_value = env::var(var_name);
|
||||||
|
debug!("checking env var: {}={:?}", var_name, var_value);
|
||||||
|
|
||||||
|
match var_value {
|
||||||
|
Ok(val) => {
|
||||||
|
// set loglevel acording to RUST_LOG
|
||||||
|
let level = match val.to_lowercase().as_str() {
|
||||||
|
"none" => LevelFilter::OFF,
|
||||||
|
"trace" => LevelFilter::TRACE,
|
||||||
|
"debug" => LevelFilter::DEBUG,
|
||||||
|
"info" => LevelFilter::INFO,
|
||||||
|
"warn" => LevelFilter::WARN,
|
||||||
|
"error" => LevelFilter::ERROR,
|
||||||
|
_ => {
|
||||||
|
log_messages.push((Level::WARN, format!(
|
||||||
|
"The value of Environment Variable {}: \"{}\" is not a valid log-level. loglevel stays at: {}"
|
||||||
|
, var_name
|
||||||
|
, val
|
||||||
|
, log_level
|
||||||
|
)));
|
||||||
|
log_level
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return (Some(level), log_messages);
|
||||||
|
}
|
||||||
|
Err(env::VarError::NotPresent) => {}
|
||||||
|
Err(env::VarError::NotUnicode(os_string)) => {
|
||||||
|
let os_string_as_hex: String = os_string
|
||||||
|
.clone()
|
||||||
|
.into_encoded_bytes()
|
||||||
|
.iter()
|
||||||
|
.map(|b| format!("{:02x}", b).to_string())
|
||||||
|
.collect::<Vec<String>>()
|
||||||
|
.join("");
|
||||||
|
log_messages.push((Level::DEBUG, format!(
|
||||||
|
"value of Environment Variable {} was not a unicode valid string. Therefore ignoring it.
|
||||||
|
\n value with replacement char U+FFFD: {}
|
||||||
|
\n the WTF-8 raw bytes as hex: {}"
|
||||||
|
, var_name
|
||||||
|
, os_string.to_string_lossy(), os_string_as_hex
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return (None, log_messages);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct MyFormatter;
|
||||||
|
|
||||||
|
impl<S, N> FormatEvent<S, N> for MyFormatter
|
||||||
|
where
|
||||||
|
S: tracing::Subscriber + for<'a> LookupSpan<'a>,
|
||||||
|
N: for<'a> FormatFields<'a> + 'static,
|
||||||
|
{
|
||||||
|
fn format_event(
|
||||||
|
&self,
|
||||||
|
ctx: &FmtContext<'_, S, N>,
|
||||||
|
mut writer: format::Writer<'_>,
|
||||||
|
event: &Event<'_>,
|
||||||
|
) -> fmt::Result {
|
||||||
|
// Format values from the event's's metadata:
|
||||||
|
|
||||||
|
let metadata = event.metadata();
|
||||||
|
|
||||||
|
// set format
|
||||||
|
//builder.format(|buf, record| match record.level() {});
|
||||||
|
|
||||||
|
let level_str = match metadata.level() {
|
||||||
|
&Level::TRACE => format!("[ {} ]", "TRACE".yellow()),
|
||||||
|
&Level::DEBUG => format!("[ {} ]", "DEBUG".green()),
|
||||||
|
&Level::INFO => format!("[ {} ]", "INFO".blue()),
|
||||||
|
&Level::WARN => format!("[ {} ]", "WARN".truecolor(245, 164, 66)),
|
||||||
|
&Level::ERROR => format!("[ {} ]", "ERROR".red()),
|
||||||
|
};
|
||||||
|
|
||||||
|
// with target
|
||||||
|
// write!(&mut writer, "{} {}: ", level_str, metadata.target())?;
|
||||||
|
|
||||||
|
write!(&mut writer, "{} ", level_str)?;
|
||||||
|
|
||||||
|
// Format all the spans in the event's span context.
|
||||||
|
if let Some(scope) = ctx.event_scope() {
|
||||||
|
for span in scope.from_root() {
|
||||||
|
write!(writer, "{}", span.name())?;
|
||||||
|
|
||||||
|
// `FormattedFields` is a formatted representation of the span's
|
||||||
|
// fields, which is stored in its extensions by the `fmt` layer's
|
||||||
|
// `new_span` method. The fields will have been formatted
|
||||||
|
// by the same field formatter that's provided to the event
|
||||||
|
// formatter in the `FmtContext`.
|
||||||
|
let ext = span.extensions();
|
||||||
|
let fields = &ext
|
||||||
|
.get::<FormattedFields<N>>()
|
||||||
|
.expect("will never be `None`");
|
||||||
|
|
||||||
|
// Skip formatting the fields if the span had no fields.
|
||||||
|
if !fields.is_empty() {
|
||||||
|
//write!(writer, "{{{}}}", fields)?;
|
||||||
|
}
|
||||||
|
write!(writer, ": ")?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write fields on the event
|
||||||
|
ctx.field_format().format_fields(writer.by_ref(), event)?;
|
||||||
|
|
||||||
|
writeln!(writer);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// thanks: https://stackoverflow.com/a/75546824
|
||||||
|
pub struct StringVisitor<'a> {
|
||||||
|
string: &'a mut String,
|
||||||
|
}
|
||||||
|
impl<'a> StringVisitor<'a> {
|
||||||
|
pub(crate) fn new(string: &'a mut String) -> Self {
|
||||||
|
StringVisitor { string }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Visit for StringVisitor<'a> {
|
||||||
|
fn record_debug(&mut self, field: &Field, value: &dyn fmt::Debug) {
|
||||||
|
if field.name() == "message" {
|
||||||
|
write!(self.string, "{value:?} ").unwrap();
|
||||||
|
} else {
|
||||||
|
write!(self.string, "{} = {:?}; ", field.name(), value).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct LogFilter {
|
||||||
|
target: Option<String>,
|
||||||
|
level: Option<Level>,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn string_to_level(string: &str) -> Option<Level> {
|
||||||
|
match string.to_lowercase().as_str() {
|
||||||
|
"info" => Some(Level::INFO),
|
||||||
|
"debug" => Some(Level::DEBUG),
|
||||||
|
"warn" | "warning" => Some(Level::WARN),
|
||||||
|
"trace" => Some(Level::TRACE),
|
||||||
|
"error" => Some(Level::ERROR),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
653
core/src/platform/os/mod.rs
Normal file
653
core/src/platform/os/mod.rs
Normal file
@@ -0,0 +1,653 @@
|
|||||||
|
use clap::ArgMatches;
|
||||||
|
use colored::Colorize;
|
||||||
|
use tracing::Dispatch;
|
||||||
|
use core::fmt;
|
||||||
|
use flate2::bufread::GzDecoder;
|
||||||
|
use sha2::{Digest, Sha256};
|
||||||
|
use std::env::VarError;
|
||||||
|
use std::fmt::Write;
|
||||||
|
use std::fs;
|
||||||
|
use std::fs::File;
|
||||||
|
use std::path::Path;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
use std::time::UNIX_EPOCH;
|
||||||
|
use tar::Archive;
|
||||||
|
use tracing::Event;
|
||||||
|
use tracing::field::Visit;
|
||||||
|
use tracing::Level;
|
||||||
|
use tracing_core::Field;
|
||||||
|
use tracing_subscriber::fmt::format;
|
||||||
|
use tracing_subscriber::fmt::FmtContext;
|
||||||
|
use tracing_subscriber::fmt::FormatEvent;
|
||||||
|
use tracing_subscriber::fmt::FormatFields;
|
||||||
|
use tracing_subscriber::fmt::FormattedFields;
|
||||||
|
use tracing_subscriber::registry::LookupSpan;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use std::sync::Mutex;
|
||||||
|
|
||||||
|
use crate::prelude::*;
|
||||||
|
use crate::item::data_from_string;
|
||||||
|
|
||||||
|
use self::fsstore::FileStore;
|
||||||
|
|
||||||
|
pub mod fsstore;
|
||||||
|
pub mod logging;
|
||||||
|
|
||||||
|
#[cfg(target_family = "unix")]
|
||||||
|
mod unix_socket;
|
||||||
|
|
||||||
|
#[cfg(target_family = "windows")]
|
||||||
|
mod windows_socket;
|
||||||
|
|
||||||
|
#[cfg(target_family = "unix")]
|
||||||
|
use crate::platform::os::unix_socket::UnixListener;
|
||||||
|
|
||||||
|
#[cfg(target_family = "windows")]
|
||||||
|
use crate::platform::os::windows_socket::WindowsListener as UnixListener;
|
||||||
|
//mod web;
|
||||||
|
|
||||||
|
pub fn os_instance_init(mize: &mut Mize) -> MizeResult<()> {
|
||||||
|
// this is the code, that runs to initialize an Instance on a system with an os present.
|
||||||
|
|
||||||
|
|
||||||
|
// setup loging to stderr if enabled
|
||||||
|
if mize
|
||||||
|
.get_config("log.stderr.enable")
|
||||||
|
.unwrap_or(ItemData::new())
|
||||||
|
.value_bool()
|
||||||
|
.unwrap_or(false)
|
||||||
|
{
|
||||||
|
use tracing::Level;
|
||||||
|
let level_string = mize
|
||||||
|
.get_config("log.level")?
|
||||||
|
.value_string()
|
||||||
|
.unwrap_or("info".to_string());
|
||||||
|
let log_level = match level_string.to_lowercase().as_str() {
|
||||||
|
"info" => Level::INFO,
|
||||||
|
"debug" => Level::DEBUG,
|
||||||
|
"warn" | "warning" => Level::WARN,
|
||||||
|
"trace" => Level::TRACE,
|
||||||
|
"error" => Level::ERROR,
|
||||||
|
_ => Level::INFO, // the default log level
|
||||||
|
};
|
||||||
|
println!("enabling loggging to stderr with log_level: {}", log_level);
|
||||||
|
|
||||||
|
// builder
|
||||||
|
let builder = tracing_subscriber::fmt::Subscriber::builder();
|
||||||
|
|
||||||
|
let field_formatter = tracing_subscriber::fmt::format::debug_fn(|writer, field, value| {
|
||||||
|
if field.name() == "message" {
|
||||||
|
write!(writer, "{:?}", value)?;
|
||||||
|
} else {
|
||||||
|
//write!(writer, "{}={:?}", field, value)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
});
|
||||||
|
builder
|
||||||
|
//.with_env_filter(my_env_filter)
|
||||||
|
.without_time()
|
||||||
|
.event_format(MyFormatter)
|
||||||
|
.fmt_fields(field_formatter)
|
||||||
|
.with_max_level(log_level)
|
||||||
|
.init();
|
||||||
|
}
|
||||||
|
|
||||||
|
////// if a config.store_path is set, upgrade to the filestore there
|
||||||
|
let store_path = match mize.get_config("store")?.value_string() {
|
||||||
|
Ok(path) => path,
|
||||||
|
Err(_e) => {
|
||||||
|
// the default store_path: $HOME/.mize
|
||||||
|
let home_dir = if let Some(dir) = std::env::home_dir() {
|
||||||
|
dir
|
||||||
|
} else {
|
||||||
|
return Err(mize_err!(
|
||||||
|
"could not get home_dir and store_path is not set in the config"
|
||||||
|
));
|
||||||
|
};
|
||||||
|
|
||||||
|
let store_path_buff = home_dir.join(".mize");
|
||||||
|
let store_path = store_path_buff.display().to_string();
|
||||||
|
|
||||||
|
mize.set_blocking(
|
||||||
|
"self/config/store_path",
|
||||||
|
store_path.clone().into_item_data(),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let store_path = mize.get("self/config/store_path")?.value_string()?;
|
||||||
|
|
||||||
|
println!("store_path: {}", store_path);
|
||||||
|
|
||||||
|
store_path
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if FileStore::store_is_opened(store_path.to_owned(), mize)? {
|
||||||
|
// if the store is already opened, connect to the instance, that opened it and join
|
||||||
|
// it's namespace
|
||||||
|
|
||||||
|
#[cfg(target_family = "unix")]
|
||||||
|
{
|
||||||
|
info!("CONNECTING");
|
||||||
|
use crate::platform::os::unix_socket;
|
||||||
|
unix_socket::connect(mize, store_path.into())?;
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_family = "windows")]
|
||||||
|
{
|
||||||
|
info!("CONNECTING");
|
||||||
|
use crate::platform::os::windows_socket;
|
||||||
|
windows_socket::connect(mize, store_path.into())?;
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// else open it ourselves
|
||||||
|
let file_store = FileStore::new(store_path.as_str())?;
|
||||||
|
mize.migrate_to_store(Box::new(file_store))?;
|
||||||
|
|
||||||
|
let path = Path::new(&store_path).to_owned();
|
||||||
|
|
||||||
|
#[cfg(target_family = "unix")]
|
||||||
|
{
|
||||||
|
mize.add_conn_listener(UnixListener::new(path)?)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_family = "windows")]
|
||||||
|
{
|
||||||
|
mize.add_conn_listener(UnixListener::new(path)?)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn seconds_since_modification(path: &Path) -> MizeResult<u64> {
|
||||||
|
let metadata = fs::metadata(path)?;
|
||||||
|
|
||||||
|
let modified = metadata.modified()?;
|
||||||
|
|
||||||
|
let duration = modified.duration_since(UNIX_EPOCH)?;
|
||||||
|
|
||||||
|
Ok(duration.as_secs())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Best-effort rustc target triple for the currently running binary.
|
||||||
|
pub fn host_rust_target() -> &'static str {
|
||||||
|
#[cfg(all(target_arch = "x86_64", target_os = "linux", target_env = "gnu"))]
|
||||||
|
{
|
||||||
|
"x86_64-unknown-linux-gnu"
|
||||||
|
}
|
||||||
|
#[cfg(all(target_arch = "aarch64", target_os = "linux", target_env = "gnu"))]
|
||||||
|
{
|
||||||
|
"aarch64-unknown-linux-gnu"
|
||||||
|
}
|
||||||
|
#[cfg(all(target_arch = "x86_64", target_os = "macos"))]
|
||||||
|
{
|
||||||
|
"x86_64-apple-darwin"
|
||||||
|
}
|
||||||
|
#[cfg(all(target_arch = "aarch64", target_os = "macos"))]
|
||||||
|
{
|
||||||
|
"aarch64-apple-darwin"
|
||||||
|
}
|
||||||
|
#[cfg(all(target_arch = "x86_64", target_os = "windows"))]
|
||||||
|
{
|
||||||
|
"x86_64-pc-windows-gnu"
|
||||||
|
}
|
||||||
|
#[cfg(all(target_arch = "aarch64", target_os = "windows"))]
|
||||||
|
{
|
||||||
|
"aarch64-pc-windows-gnullvm"
|
||||||
|
}
|
||||||
|
#[cfg(not(any(
|
||||||
|
all(target_arch = "x86_64", target_os = "linux", target_env = "gnu"),
|
||||||
|
all(target_arch = "aarch64", target_os = "linux", target_env = "gnu"),
|
||||||
|
all(target_arch = "x86_64", target_os = "macos"),
|
||||||
|
all(target_arch = "aarch64", target_os = "macos"),
|
||||||
|
all(target_arch = "x86_64", target_os = "windows"),
|
||||||
|
all(target_arch = "aarch64", target_os = "windows"),
|
||||||
|
)))]
|
||||||
|
{
|
||||||
|
"unknown"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Dist artifact filename: `{hash}-{part_id}-cd.tar.gz` or `{hash}-{part_id}-{version}.tar.gz`.
|
||||||
|
pub fn part_dist_artifact_name(hash: &str, part_id: &str, version: &str, cd: bool) -> String {
|
||||||
|
if cd {
|
||||||
|
format!("{hash}-{part_id}-cd.tar.gz")
|
||||||
|
} else {
|
||||||
|
format!("{hash}-{part_id}-{version}.tar.gz")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parts_dist_cd(mize: &mut Mize) -> bool {
|
||||||
|
mize.get_config("parts.dist.cd")
|
||||||
|
.ok()
|
||||||
|
.and_then(|d| d.value_bool().ok())
|
||||||
|
.unwrap_or(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Read `version` from a Cargo.toml's `[package]` table.
|
||||||
|
pub fn cargo_package_version(cargo_toml_path: &str) -> MizeResult<String> {
|
||||||
|
let contents = fs::read_to_string(cargo_toml_path)
|
||||||
|
.map_err(|e| mize_err!("could not read {cargo_toml_path}: {e}"))?;
|
||||||
|
let data = ItemData::from_toml(&contents)?;
|
||||||
|
data.get_path("package.version")?
|
||||||
|
.value_string()
|
||||||
|
.map_err(|e| e.msg(format!("no package.version in {cargo_toml_path}")))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Resolve a part's cargo version for dist URLs (non-cd).
|
||||||
|
pub fn resolve_part_version(mize: &mut Mize, part_name: &str) -> MizeResult<String> {
|
||||||
|
if let Ok(v) = mize.get_config(&format!("{part_name}.version")) {
|
||||||
|
if let Ok(s) = v.value_string() {
|
||||||
|
if !s.is_empty() {
|
||||||
|
return Ok(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if part_name == "mize" {
|
||||||
|
return Ok(env!("CARGO_PKG_VERSION").to_owned());
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Ok(workspaces) = mize.get_config("parts.workspaces") {
|
||||||
|
if let Some(arr) = workspaces.cbor().as_array() {
|
||||||
|
for src in arr {
|
||||||
|
let Some(dir) = src.as_text() else { continue };
|
||||||
|
for candidate in [
|
||||||
|
format!("{dir}/{part_name}/Cargo.toml"),
|
||||||
|
format!("{dir}/mize.{part_name}/Cargo.toml"),
|
||||||
|
] {
|
||||||
|
if PathBuf::from(&candidate).exists() {
|
||||||
|
return cargo_package_version(&candidate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Ok(mize_src) = mize.get_config("mize.src").and_then(|d| d.value_string()) {
|
||||||
|
for candidate in [
|
||||||
|
format!("{mize_src}/parts/{part_name}/Cargo.toml"),
|
||||||
|
format!("{mize_src}/parts/mize.{part_name}/Cargo.toml"),
|
||||||
|
format!("{mize_src}/core/Cargo.toml"),
|
||||||
|
] {
|
||||||
|
if part_name != "mize" && candidate.ends_with("core/Cargo.toml") {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if PathBuf::from(&candidate).exists() {
|
||||||
|
return cargo_package_version(&candidate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Err(mize_err!(
|
||||||
|
"could not resolve cargo version for part '{part_name}' (set {part_name}.version or parts.workspaces, or enable parts.dist.cd)"
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn load_selector_base(mize: &mut Mize) -> ItemData {
|
||||||
|
// Prefer config opt; fall back to store path used by tests / older setups.
|
||||||
|
if let Ok(sel) = mize.get("self/config/selector").and_then(|i| i.as_data_full()) {
|
||||||
|
if !sel.cbor().is_null() {
|
||||||
|
return sel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mize.get("0/config/selector")
|
||||||
|
.and_then(|i| i.as_data_full())
|
||||||
|
.unwrap_or_else(|_| ItemData::new())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_part_hash(
|
||||||
|
mize: &mut Mize,
|
||||||
|
name: &str,
|
||||||
|
options: ItemData,
|
||||||
|
) -> MizeResult<(String, String)> {
|
||||||
|
let mut selector_data = load_selector_base(mize);
|
||||||
|
|
||||||
|
if selector_data
|
||||||
|
.get_path("mize_version")
|
||||||
|
.ok()
|
||||||
|
.and_then(|v| v.value_string().ok())
|
||||||
|
.filter(|s| !s.is_empty())
|
||||||
|
.is_none()
|
||||||
|
{
|
||||||
|
selector_data.set_path("mize_version", env!("CARGO_PKG_VERSION"))?;
|
||||||
|
}
|
||||||
|
if selector_data
|
||||||
|
.get_path("system")
|
||||||
|
.ok()
|
||||||
|
.and_then(|v| v.value_string().ok())
|
||||||
|
.filter(|s| !s.is_empty())
|
||||||
|
.is_none()
|
||||||
|
{
|
||||||
|
selector_data.set_path("system", host_rust_target())?;
|
||||||
|
}
|
||||||
|
|
||||||
|
// handle the case, when we want a part for not the system we are running on...
|
||||||
|
debug!("get_part_hash: name: {}", name);
|
||||||
|
let mut name_parts = name.split(".");
|
||||||
|
if name_parts
|
||||||
|
.next()
|
||||||
|
.ok_or(mize_err!("no 0th element in the modName"))?
|
||||||
|
== "cross"
|
||||||
|
{
|
||||||
|
let system = name_parts
|
||||||
|
.next()
|
||||||
|
.ok_or(mize_err!("no 1th element in the modName"))?;
|
||||||
|
let mod_name: String = name_parts.collect();
|
||||||
|
println!("get_part_hash: mod_name: {}", mod_name);
|
||||||
|
if mod_name == "" {
|
||||||
|
return Err(mize_err!("no 2nd element in the modName"));
|
||||||
|
}
|
||||||
|
|
||||||
|
selector_data.set_path("system", system)?;
|
||||||
|
selector_data.set_path("modName", mod_name)?;
|
||||||
|
} else {
|
||||||
|
selector_data.set_path("modName", name)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
selector_data.merge(options);
|
||||||
|
|
||||||
|
// important to produce the same hash for the sama selector data
|
||||||
|
selector_data.sort_keys();
|
||||||
|
|
||||||
|
let selector_str = selector_data.to_json()?;
|
||||||
|
|
||||||
|
let mut hasher = Sha256::new();
|
||||||
|
|
||||||
|
hasher.update(selector_str.clone().as_bytes());
|
||||||
|
|
||||||
|
let result = hasher.finalize();
|
||||||
|
|
||||||
|
let mut hex: String = result
|
||||||
|
.iter()
|
||||||
|
.map(|b| format!("{:02x}", b).to_string())
|
||||||
|
.collect::<Vec<String>>()
|
||||||
|
.join("");
|
||||||
|
|
||||||
|
hex.truncate(32);
|
||||||
|
|
||||||
|
return Ok((hex, selector_str));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn fetch_part(mize: &mut Mize, part_name: &str) -> MizeResult<String> {
|
||||||
|
let (part_hash, part_selector) = get_part_hash(mize, part_name, ItemData::new())?;
|
||||||
|
let cd = parts_dist_cd(mize);
|
||||||
|
let version = if cd {
|
||||||
|
String::new()
|
||||||
|
} else {
|
||||||
|
resolve_part_version(mize, part_name)?
|
||||||
|
};
|
||||||
|
let artifact = part_dist_artifact_name(&part_hash, part_name, &version, cd);
|
||||||
|
let part_url = format!("https://dist.mize.works/{artifact}");
|
||||||
|
|
||||||
|
let store_path = mize.get_config("store")?.value_string()?;
|
||||||
|
let parts_path = format!("{store_path}/parts");
|
||||||
|
let target_dir = format!("{parts_path}/{part_hash}-{part_name}");
|
||||||
|
|
||||||
|
let tmp_file_path_gz = format!("{parts_path}/{artifact}");
|
||||||
|
std::fs::create_dir_all(&parts_path)?;
|
||||||
|
|
||||||
|
let tmp_gz_file = if !PathBuf::from(tmp_file_path_gz.clone()).exists() {
|
||||||
|
let mut tmp_gz_file = File::create(&tmp_file_path_gz)?;
|
||||||
|
|
||||||
|
debug!(
|
||||||
|
"downloading part '{}' from {} (selector: {})",
|
||||||
|
part_name, part_url, part_selector
|
||||||
|
);
|
||||||
|
|
||||||
|
let response = http_req::request::get(&part_url, &mut tmp_gz_file)?;
|
||||||
|
|
||||||
|
let code = response.status_code();
|
||||||
|
if code != 200.into() {
|
||||||
|
return Err(mize_err!(
|
||||||
|
"failed to get part '{}' with hash '{}' and selector: {}",
|
||||||
|
part_name,
|
||||||
|
part_hash,
|
||||||
|
part_selector
|
||||||
|
)
|
||||||
|
.msg(format!(
|
||||||
|
"the http request to fetch the part from '{}' returned a status code of: {}",
|
||||||
|
part_url, code
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
tmp_gz_file
|
||||||
|
} else {
|
||||||
|
std::fs::File::open(tmp_file_path_gz.clone())?
|
||||||
|
};
|
||||||
|
|
||||||
|
let tar = GzDecoder::new(std::io::BufReader::new(tmp_gz_file));
|
||||||
|
let mut archive = Archive::new(tar);
|
||||||
|
|
||||||
|
//archive.set_mask(umask::Mode::parse("rwxrwxrwx")?.into());
|
||||||
|
|
||||||
|
std::fs::create_dir_all(target_dir.as_str())?;
|
||||||
|
|
||||||
|
archive.unpack(target_dir.as_str())?;
|
||||||
|
|
||||||
|
fs::remove_file(&tmp_file_path_gz)?;
|
||||||
|
|
||||||
|
Ok(format!("{parts_path}/{part_hash}-{part_name}"))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_part_path(mize: &mut Mize, part_name: &str, _path: Option<String>) -> MizeResult<String> {
|
||||||
|
|
||||||
|
let part_symbol_name = part_name.replace(".", "_");
|
||||||
|
|
||||||
|
// check if $name.src is set
|
||||||
|
if let Ok(itm) = mize.get_config(format!("{part_name}.src").as_str()) {
|
||||||
|
let part_src = itm.value_string().map_err(|e| e.msg(format!("opt {part_name}.src was not a string")))?;
|
||||||
|
// will only work for rust for now
|
||||||
|
let part_path = format!("{part_src}/target/debug/libmize_part_{part_symbol_name}.so");
|
||||||
|
return Ok(part_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if $name.out is set
|
||||||
|
if let Ok(itm) = mize.get_config(format!("{part_name}.out").as_str()) {
|
||||||
|
let part_out = itm.value_string().map_err(|e| e.msg(format!("opt {part_name}.out was not a string")))?;
|
||||||
|
let part_path = format!("{part_out}/lib/libmize_part_{part_symbol_name}.so");
|
||||||
|
return Ok(part_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if we find the part inside mize.src
|
||||||
|
if let Ok(itm) = mize.get_config("mize.src") {
|
||||||
|
let mize_src = itm.value_string().map_err(|e| e.msg("opt mize.src was not a string"))?;
|
||||||
|
let part_path = format!("{mize_src}/target/debug/libmize_part_{part_symbol_name}.so");
|
||||||
|
return Ok(part_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
// fetch the part from https://mize.works
|
||||||
|
let out_path = fetch_part(mize, part_name)?;
|
||||||
|
return Ok(format!("{out_path}/lib/libmize_part_{part_symbol_name}.so"));
|
||||||
|
|
||||||
|
//Err(mize_err!("part {part_name} not found"))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Load a part `.so` for `part_name`, registering it under `at_id` (or `part_name` if None).
|
||||||
|
/// Uses `get_global_mize()` so nested loads from inside another part's init work.
|
||||||
|
pub fn load_part(part_name: &str, path: Option<String>, at_id: Option<&str>) -> MizeResult<()> {
|
||||||
|
let mut mize = crate::get_global_mize()?;
|
||||||
|
let part_path = get_part_path(&mut mize, part_name, path)?;
|
||||||
|
|
||||||
|
let lib = unsafe { libloading::Library::new(part_path)? };
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
if let Ok(rust_hack_func) = lib.get::<libloading::Symbol<unsafe extern "C" fn(Arc<Mutex<Mize>>, *const std::ffi::c_void)>>("mize_rust_hack_set_global".as_bytes()) {
|
||||||
|
|
||||||
|
tracing::dispatcher::get_default(|dispatch| {
|
||||||
|
rust_hack_func(GLOBAL_MIZE.get().unwrap().clone(), dispatch as *const Dispatch as *const std::ffi::c_void);
|
||||||
|
});
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
let part_symbol_name = part_name.replace(".", "_");
|
||||||
|
|
||||||
|
let func: libloading::Symbol<unsafe extern "C" fn()> =
|
||||||
|
unsafe { lib.get(format!("part_{}", part_symbol_name).as_bytes())? };
|
||||||
|
|
||||||
|
let eval_name = at_id.unwrap_or(part_name);
|
||||||
|
let part_id = mize.clone().id_new_dots(eval_name)?;
|
||||||
|
{
|
||||||
|
let mut opts = mize.config_opts.lock()?;
|
||||||
|
opts.entry(part_id.clone()).or_insert_with(|| ConfigOpt {
|
||||||
|
name: eval_name.to_owned(),
|
||||||
|
val: Some(ItemData::new()),
|
||||||
|
fun: None,
|
||||||
|
deps: Vec::new(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let prev_eval = mize.clone().cur_eval_part()?;
|
||||||
|
mize.clone().set_cur_eval_part(Some(part_id))?;
|
||||||
|
crate::c_api::clear_err();
|
||||||
|
unsafe { func() };
|
||||||
|
let c_err = crate::c_api::take_err();
|
||||||
|
mize.clone().set_cur_eval_part(prev_eval)?;
|
||||||
|
|
||||||
|
// dropping the lib, would (i suspect) free all memory, of the library's code, which would
|
||||||
|
// make the part's vtable point into empty memory
|
||||||
|
std::mem::forget(lib);
|
||||||
|
|
||||||
|
if let Some(err) = c_err {
|
||||||
|
return Err(err);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn config_from_cli_args(matches: &ArgMatches) -> MizeResult<ItemData> {
|
||||||
|
let mut config = ItemData::new();
|
||||||
|
|
||||||
|
////// populate values from $MIZE_CONFIG_FILES
|
||||||
|
if let Ok(config_file_paths) = std::env::var("MIZE_CONFIG_FILES") {
|
||||||
|
for config_file_path in config_file_paths.split(":") {
|
||||||
|
println!("reading config file: {config_file_path}");
|
||||||
|
let content = std::fs::read_to_string(config_file_path)?;
|
||||||
|
let data = ItemData::from_toml(content.as_str())?;
|
||||||
|
config.merge(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
////// load config from MIZE_CONFIG env var
|
||||||
|
match std::env::var("MIZE_CONFIG") {
|
||||||
|
Ok(config_string) => {
|
||||||
|
let data = data_from_string(config_string)?;
|
||||||
|
config.merge(data);
|
||||||
|
}
|
||||||
|
Err(var_err) => match var_err {
|
||||||
|
VarError::NotPresent => {
|
||||||
|
debug!("env var MIZE_CONFIG NOT present")
|
||||||
|
}
|
||||||
|
VarError::NotUnicode(_) => {
|
||||||
|
warn!("env var MIZE_CONFIG is not Unicode, so not reading it")
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(config_file_path) = matches.get_one::<String>("config") {
|
||||||
|
config.merge(config_from_file(config_file_path.to_string())?);
|
||||||
|
trace!("config after --config arg: {}", config);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(config_string) = matches.get_one::<String>("opt") {
|
||||||
|
config.merge(data_from_string(config_string.to_string())?);
|
||||||
|
trace!("config after --opt arg: {}", config);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ok(config);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn config_from_file(file_path: String) -> MizeResult<ItemData> {
|
||||||
|
let toml_string = fs::read_to_string(&file_path)
|
||||||
|
.mres_msg(format!("Could not read file {} as mize config", &file_path))?;
|
||||||
|
|
||||||
|
let item_data = ItemData::from_toml(toml_string.as_str())?;
|
||||||
|
|
||||||
|
return Ok(item_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct MyFormatter;
|
||||||
|
|
||||||
|
impl<S, N> FormatEvent<S, N> for MyFormatter
|
||||||
|
where
|
||||||
|
S: tracing::Subscriber + for<'a> LookupSpan<'a>,
|
||||||
|
N: for<'a> FormatFields<'a> + 'static,
|
||||||
|
{
|
||||||
|
fn format_event(
|
||||||
|
&self,
|
||||||
|
ctx: &FmtContext<'_, S, N>,
|
||||||
|
mut writer: format::Writer<'_>,
|
||||||
|
event: &Event<'_>,
|
||||||
|
) -> fmt::Result {
|
||||||
|
// Format values from the event's's metadata:
|
||||||
|
|
||||||
|
let metadata = event.metadata();
|
||||||
|
|
||||||
|
// set format
|
||||||
|
//builder.format(|buf, record| match record.level() {});
|
||||||
|
|
||||||
|
let level_str = match metadata.level() {
|
||||||
|
&Level::TRACE => format!("[ {} ]", "TRACE".yellow()),
|
||||||
|
&Level::DEBUG => format!("[ {} ]", "DEBUG".green()),
|
||||||
|
&Level::INFO => format!("[ {} ]", "INFO".blue()),
|
||||||
|
&Level::WARN => format!("[ {} ]", "WARN".truecolor(245, 164, 66)),
|
||||||
|
&Level::ERROR => format!("[ {} ]", "ERROR".red()),
|
||||||
|
};
|
||||||
|
|
||||||
|
// with target
|
||||||
|
// write!(&mut writer, "{} {}: ", level_str, metadata.target())?;
|
||||||
|
|
||||||
|
write!(&mut writer, "{} ", level_str)?;
|
||||||
|
|
||||||
|
// Format all the spans in the event's span context.
|
||||||
|
if let Some(scope) = ctx.event_scope() {
|
||||||
|
for span in scope.from_root() {
|
||||||
|
write!(writer, "{}", span.name())?;
|
||||||
|
|
||||||
|
// `FormattedFields` is a formatted representation of the span's
|
||||||
|
// fields, which is stored in its extensions by the `fmt` layer's
|
||||||
|
// `new_span` method. The fields will have been formatted
|
||||||
|
// by the same field formatter that's provided to the event
|
||||||
|
// formatter in the `FmtContext`.
|
||||||
|
let ext = span.extensions();
|
||||||
|
let fields = &ext
|
||||||
|
.get::<FormattedFields<N>>()
|
||||||
|
.expect("will never be `None`");
|
||||||
|
|
||||||
|
// Skip formatting the fields if the span had no fields.
|
||||||
|
if !fields.is_empty() {
|
||||||
|
//write!(writer, "{{{}}}", fields)?;
|
||||||
|
}
|
||||||
|
write!(writer, ": ")?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write fields on the event
|
||||||
|
ctx.field_format().format_fields(writer.by_ref(), event)?;
|
||||||
|
|
||||||
|
writeln!(writer);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// thanks: https://stackoverflow.com/a/75546824
|
||||||
|
pub struct StringVisitor<'a> {
|
||||||
|
string: &'a mut String,
|
||||||
|
}
|
||||||
|
impl<'a> StringVisitor<'a> {
|
||||||
|
pub(crate) fn new(string: &'a mut String) -> Self {
|
||||||
|
StringVisitor { string }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Visit for StringVisitor<'a> {
|
||||||
|
fn record_debug(&mut self, field: &Field, value: &dyn fmt::Debug) {
|
||||||
|
if field.name() == "message" {
|
||||||
|
write!(self.string, "{value:?} ").unwrap();
|
||||||
|
} else {
|
||||||
|
write!(self.string, "{} = {:?}; ", field.name(), value).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,12 +5,12 @@ use std::path::{Path, PathBuf};
|
|||||||
use tokio::io::{AsyncReadExt, AsyncWriteExt, Interest};
|
use tokio::io::{AsyncReadExt, AsyncWriteExt, Interest};
|
||||||
use tokio::net::unix::{OwnedReadHalf, OwnedWriteHalf};
|
use tokio::net::unix::{OwnedReadHalf, OwnedWriteHalf};
|
||||||
use tokio::net::{UnixListener as TokioUnixListener, UnixStream};
|
use tokio::net::{UnixListener as TokioUnixListener, UnixStream};
|
||||||
use tracing::{debug, info, warn};
|
|
||||||
|
|
||||||
use crate::error::{IntoMizeResult, MizeError, MizeResult};
|
use crate::error::{IntoMizeResult, MizeError, MizeResult};
|
||||||
use crate::instance::connection::{ConnListener, Connection};
|
use crate::instance::connection::{ConnListener, Connection};
|
||||||
use crate::instance::{self, Mize};
|
use crate::instance::{self, Mize};
|
||||||
use crate::proto::{self, MizeMessage};
|
use crate::proto::{self, MizeMessage};
|
||||||
|
use crate::prelude::*;
|
||||||
|
|
||||||
pub struct UnixListener {
|
pub struct UnixListener {
|
||||||
sock_path: PathBuf,
|
sock_path: PathBuf,
|
||||||
@@ -32,7 +32,7 @@ pub fn connect(instance: &mut Mize, store_path: PathBuf) -> MizeResult<()> {
|
|||||||
|
|
||||||
let ns_of_peer_str = instance
|
let ns_of_peer_str = instance
|
||||||
.get(format!(
|
.get(format!(
|
||||||
"inst/con_by_id/{}/peer/0/config/namespace",
|
"inst/con_by_id/{}/peer/self/config/namespace",
|
||||||
conn_id
|
conn_id
|
||||||
))?
|
))?
|
||||||
.value_string()?;
|
.value_string()?;
|
||||||
@@ -91,7 +91,7 @@ async fn unix_listen(listener: UnixListener, mut instance: Mize) -> MizeResult<(
|
|||||||
// remove the file at sock_path if it already exists
|
// remove the file at sock_path if it already exists
|
||||||
fs::remove_file(&listener.sock_path);
|
fs::remove_file(&listener.sock_path);
|
||||||
|
|
||||||
let listener = TokioUnixListener::bind(&listener.sock_path).mize_result_msg(format!(
|
let listener = TokioUnixListener::bind(&listener.sock_path).mres_msg(format!(
|
||||||
"Could not bind to unix socket at '{}'",
|
"Could not bind to unix socket at '{}'",
|
||||||
listener.sock_path.display()
|
listener.sock_path.display()
|
||||||
))?;
|
))?;
|
||||||
@@ -100,7 +100,7 @@ async fn unix_listen(listener: UnixListener, mut instance: Mize) -> MizeResult<(
|
|||||||
let (mut unix_sock, addr) = listener
|
let (mut unix_sock, addr) = listener
|
||||||
.accept()
|
.accept()
|
||||||
.await
|
.await
|
||||||
.mize_result_msg("Error while accepting Unix sock connection")?;
|
.mres_msg("Error while accepting Unix sock connection")?;
|
||||||
info!("new connection");
|
info!("new connection");
|
||||||
|
|
||||||
let (send_tx, send_rx) = unbounded::<MizeMessage>();
|
let (send_tx, send_rx) = unbounded::<MizeMessage>();
|
||||||
@@ -147,7 +147,7 @@ fn unix_outgoing(
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn unix_incomming(
|
pub fn unix_incomming(
|
||||||
mut unix_read: OwnedReadHalf,
|
mut unix_read: OwnedReadHalf,
|
||||||
mut instance: Mize,
|
mut instance: Mize,
|
||||||
conn_id: u64,
|
conn_id: u64,
|
||||||
224
core/src/platform/os/windows_socket.rs
Normal file
224
core/src/platform/os/windows_socket.rs
Normal file
@@ -0,0 +1,224 @@
|
|||||||
|
// This module is only compiled on Windows
|
||||||
|
// It provides the same functionality as unix_socket.rs but using Windows named pipes
|
||||||
|
|
||||||
|
use ciborium::Value as CborValue;
|
||||||
|
use flume::{unbounded, Receiver, Sender};
|
||||||
|
use std::path::PathBuf;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
||||||
|
use tokio::net::windows::named_pipe::{ClientOptions, ServerOptions};
|
||||||
|
use tokio::sync::Mutex;
|
||||||
|
use tracing::{debug, info, warn};
|
||||||
|
|
||||||
|
use crate::error::{IntoMizeResult, MizeError, MizeResult};
|
||||||
|
use crate::instance::connection::{ConnListener, Connection};
|
||||||
|
use crate::instance::{self, Mize};
|
||||||
|
use crate::proto::{self, MizeMessage};
|
||||||
|
|
||||||
|
pub struct WindowsListener {
|
||||||
|
pipe_name: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WindowsListener {
|
||||||
|
pub fn new(store_path: PathBuf) -> MizeResult<WindowsListener> {
|
||||||
|
// On Windows, we use a named pipe with a specific format
|
||||||
|
// The format is \.\pipe\<name> for local pipes
|
||||||
|
// We'll use the store path hash or name to create a unique pipe name
|
||||||
|
let pipe_name = format!(
|
||||||
|
"\\.\\pipe\\mize_{}",
|
||||||
|
store_path.to_string_lossy().replace('\\', "_").replace('/', "_").replace(':', "_")
|
||||||
|
);
|
||||||
|
Ok(WindowsListener { pipe_name })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn connect(instance: &mut Mize, store_path: PathBuf) -> MizeResult<()> {
|
||||||
|
let conn_id = instance.spawn_async_blocking(
|
||||||
|
"windows_connection",
|
||||||
|
connect_async(instance.clone(), store_path),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let ns_of_peer_str = instance
|
||||||
|
.get(format!(
|
||||||
|
"inst/con_by_id/{}/peer/self/config/namespace",
|
||||||
|
conn_id
|
||||||
|
))?
|
||||||
|
.value_string()?;
|
||||||
|
let ns_of_peer = instance.namespace_from_string(ns_of_peer_str)?;
|
||||||
|
|
||||||
|
info!(
|
||||||
|
"connect_async ... ns_of_peer: {}",
|
||||||
|
ns_of_peer.clone().as_string()
|
||||||
|
);
|
||||||
|
|
||||||
|
instance.connection_set_namespace(conn_id, ns_of_peer.clone());
|
||||||
|
instance.set_namespace(ns_of_peer);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn connect_async(mut instance: Mize, store_path: PathBuf) -> MizeResult<u64> {
|
||||||
|
let pipe_name = format!(
|
||||||
|
"\\.\\pipe\\mize_{}",
|
||||||
|
store_path.to_string_lossy().replace('\\', "_").replace('/', "_").replace(':', "_")
|
||||||
|
);
|
||||||
|
|
||||||
|
// Create the client synchronously
|
||||||
|
let client = ClientOptions::new().open(&pipe_name)?;
|
||||||
|
|
||||||
|
// Wrap the client in an Arc<Mutex> so it can be shared between incoming and outgoing tasks
|
||||||
|
let shared_client = Arc::new(Mutex::new(client));
|
||||||
|
|
||||||
|
let (send_tx, send_rx) = unbounded::<MizeMessage>();
|
||||||
|
|
||||||
|
let conn_id = instance.new_connection(send_tx)?;
|
||||||
|
|
||||||
|
let cloned_instance = instance.clone();
|
||||||
|
let incoming_client = shared_client.clone();
|
||||||
|
instance.spawn_background("incomming", move || {
|
||||||
|
let result = windows_incomming(incoming_client, cloned_instance, conn_id);
|
||||||
|
if let Err(err) = result {
|
||||||
|
warn!("Connection closing because of EOF: {:?}", err);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
});
|
||||||
|
|
||||||
|
let outgoing_cloned_instance = instance.clone();
|
||||||
|
let outgoing_client = shared_client.clone();
|
||||||
|
instance.spawn_background("outgoing", move || {
|
||||||
|
let result = windows_outgoing(outgoing_client, send_rx, outgoing_cloned_instance, conn_id);
|
||||||
|
if let Err(err) = result {
|
||||||
|
warn!("Connection closing because of EOF: {:?}", err);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(conn_id)
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ConnListener for WindowsListener {
|
||||||
|
fn listen(self, mut instance: Mize) -> MizeResult<()> {
|
||||||
|
instance.spawn_async("windows listen async", windows_listen(self, instance.clone()));
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn windows_listen(listener: WindowsListener, mut instance: Mize) -> MizeResult<()> {
|
||||||
|
// Create the first server instance
|
||||||
|
let mut server = ServerOptions::new()
|
||||||
|
.first_pipe_instance(true)
|
||||||
|
.create(&listener.pipe_name)?;
|
||||||
|
|
||||||
|
loop {
|
||||||
|
// Wait for a client to connect
|
||||||
|
server.connect().await.mres_msg("Error while accepting Windows named pipe connection")?;
|
||||||
|
info!("new connection");
|
||||||
|
|
||||||
|
let (send_tx, send_rx) = unbounded::<MizeMessage>();
|
||||||
|
let conn_id = instance.new_connection(send_tx)?;
|
||||||
|
|
||||||
|
// Take ownership of the current server (which is now connected)
|
||||||
|
let connected_server = server;
|
||||||
|
|
||||||
|
// Create a new server for the next connection
|
||||||
|
server = ServerOptions::new().create(&listener.pipe_name)?;
|
||||||
|
|
||||||
|
// Wrap the connected server in an Arc<Mutex> so it can be shared
|
||||||
|
let shared_server = Arc::new(Mutex::new(connected_server));
|
||||||
|
|
||||||
|
let cloned_instance = instance.clone();
|
||||||
|
let incoming_server = shared_server.clone();
|
||||||
|
instance.spawn_background("incomming", move || {
|
||||||
|
let result = windows_incomming(incoming_server, cloned_instance, conn_id);
|
||||||
|
if let Err(err) = result {
|
||||||
|
warn!("Connection closing because of EOF: {:?}", err);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
});
|
||||||
|
|
||||||
|
let outgoing_cloned_instance = instance.clone();
|
||||||
|
let outgoing_server = shared_server.clone();
|
||||||
|
instance.spawn_background("outgoing", move || {
|
||||||
|
let result = windows_outgoing(outgoing_server, send_rx, outgoing_cloned_instance, conn_id);
|
||||||
|
if let Err(err) = result {
|
||||||
|
warn!("Connection closing because of EOF: {:?}", err);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn windows_outgoing<W>(shared_pipe: Arc<Mutex<W>>, send_rx: Receiver<MizeMessage>, mut instance: Mize, conn_id: u64) -> MizeResult<()>
|
||||||
|
where
|
||||||
|
W: AsyncWriteExt + Unpin,
|
||||||
|
{
|
||||||
|
for msg in send_rx {
|
||||||
|
debug!("windows outgoing got msg: {}", msg);
|
||||||
|
let adapter = MyCiboriumWriter {
|
||||||
|
inner: &mut *shared_pipe.blocking_lock(),
|
||||||
|
instance: &mut instance,
|
||||||
|
};
|
||||||
|
let value = msg.value();
|
||||||
|
ciborium::into_writer(&value, adapter)?
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn windows_incomming<R>(shared_pipe: Arc<Mutex<R>>, mut instance: Mize, conn_id: u64) -> MizeResult<()>
|
||||||
|
where
|
||||||
|
R: AsyncReadExt + Unpin,
|
||||||
|
{
|
||||||
|
loop {
|
||||||
|
let adapter = MyCiboriumReader {
|
||||||
|
inner: &mut *shared_pipe.blocking_lock(),
|
||||||
|
instance: &mut instance,
|
||||||
|
};
|
||||||
|
let value: CborValue = ciborium::from_reader(adapter)?;
|
||||||
|
if let CborValue::Integer(_) = value {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
let msg = MizeMessage::new(value, conn_id);
|
||||||
|
debug!("windows incomming got msg: {}", msg);
|
||||||
|
instance.got_msg(msg);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
struct MyCiboriumWriter<'a, W> {
|
||||||
|
inner: &'a mut W,
|
||||||
|
instance: &'a mut Mize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, W> ciborium_io::Write for MyCiboriumWriter<'a, W>
|
||||||
|
where
|
||||||
|
W: AsyncWriteExt + Unpin,
|
||||||
|
{
|
||||||
|
type Error = MizeError;
|
||||||
|
fn write_all(&mut self, data: &[u8]) -> Result<(), Self::Error> {
|
||||||
|
let runtime = self.instance.runtime.lock()?;
|
||||||
|
let _ = runtime.block_on(self.inner.write_all(data))?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
fn flush(&mut self) -> Result<(), Self::Error> {
|
||||||
|
let handle = self.instance.async_get_handle();
|
||||||
|
let _ = handle.block_on(self.inner.flush());
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct MyCiboriumReader<'a, R> {
|
||||||
|
inner: &'a mut R,
|
||||||
|
instance: &'a mut Mize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, R> ciborium_io::Read for MyCiboriumReader<'a, R>
|
||||||
|
where
|
||||||
|
R: AsyncReadExt + Unpin,
|
||||||
|
{
|
||||||
|
type Error = MizeError;
|
||||||
|
fn read_exact(&mut self, data: &mut [u8]) -> Result<(), Self::Error> {
|
||||||
|
let handle = self.instance.async_get_handle();
|
||||||
|
let _ = handle.block_on(self.inner.read_exact(data))?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -17,6 +17,17 @@ pub struct MizeMessage {
|
|||||||
pub conn_id: u64,
|
pub conn_id: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl MizeMessage {
|
||||||
|
pub fn bytes(&self) -> Vec<u8> {
|
||||||
|
let mut writer = Vec::new();
|
||||||
|
ciborium::into_writer(&self.value, &mut writer).unwrap();
|
||||||
|
writer
|
||||||
|
}
|
||||||
|
pub fn into_writer(&self, writer: &mut impl io::Write) -> MizeResult<()> {
|
||||||
|
Ok(ciborium::into_writer(&self.value, writer)?)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// msg items
|
// msg items
|
||||||
static MSG_CMD: u16 = 1;
|
static MSG_CMD: u16 = 1;
|
||||||
static MSG_ID: u16 = 2;
|
static MSG_ID: u16 = 2;
|
||||||
@@ -42,6 +53,7 @@ pub enum MessageCmd {
|
|||||||
UpdateRequest,
|
UpdateRequest,
|
||||||
GetSub,
|
GetSub,
|
||||||
Sub,
|
Sub,
|
||||||
|
Custom(ItemData),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MizeMessage {
|
impl MizeMessage {
|
||||||
@@ -49,10 +61,28 @@ impl MizeMessage {
|
|||||||
MizeMessage { value, conn_id }
|
MizeMessage { value, conn_id }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn new_custom<T: IntoItemData, U: IntoItemData>(
|
||||||
|
custom_cmd: T,
|
||||||
|
value: U,
|
||||||
|
conn_id: u64,
|
||||||
|
) -> MizeMessage {
|
||||||
|
let value = value.into_item_data();
|
||||||
|
let custom_cmd = custom_cmd.into_item_data();
|
||||||
|
|
||||||
|
let cmd = (CborValue::Integer(MSG_CMD.into()), custom_cmd.0);
|
||||||
|
let data = (CborValue::Integer(MSG_DATA.into()), value.cbor().to_owned());
|
||||||
|
let msg_value = CborValue::Map(vec![cmd, data]);
|
||||||
|
|
||||||
|
MizeMessage {
|
||||||
|
value: msg_value,
|
||||||
|
conn_id,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn new_get(id: MizeId, conn_id: u64) -> MizeMessage {
|
pub fn new_get(id: MizeId, conn_id: u64) -> MizeMessage {
|
||||||
let id_path = id
|
let id_path = id
|
||||||
.path()
|
.path()
|
||||||
.into_iter()
|
.iter()
|
||||||
.map(|string| CborValue::Text(string.to_owned()))
|
.map(|string| CborValue::Text(string.to_owned()))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
@@ -69,7 +99,7 @@ impl MizeMessage {
|
|||||||
pub fn new_get_sub(id: MizeId, conn_id: u64) -> MizeMessage {
|
pub fn new_get_sub(id: MizeId, conn_id: u64) -> MizeMessage {
|
||||||
let id_path = id
|
let id_path = id
|
||||||
.path()
|
.path()
|
||||||
.into_iter()
|
.iter()
|
||||||
.map(|string| CborValue::Text(string.to_owned()))
|
.map(|string| CborValue::Text(string.to_owned()))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
@@ -86,7 +116,7 @@ impl MizeMessage {
|
|||||||
pub fn new_sub(id: MizeId, conn_id: u64) -> MizeMessage {
|
pub fn new_sub(id: MizeId, conn_id: u64) -> MizeMessage {
|
||||||
let id_path = id
|
let id_path = id
|
||||||
.path()
|
.path()
|
||||||
.into_iter()
|
.iter()
|
||||||
.map(|string| CborValue::Text(string.to_owned()))
|
.map(|string| CborValue::Text(string.to_owned()))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
@@ -113,7 +143,7 @@ impl MizeMessage {
|
|||||||
pub fn new_create_reply(id: MizeId, conn_id: u64) -> MizeMessage {
|
pub fn new_create_reply(id: MizeId, conn_id: u64) -> MizeMessage {
|
||||||
let id_path = id
|
let id_path = id
|
||||||
.path()
|
.path()
|
||||||
.into_iter()
|
.iter()
|
||||||
.map(|string| CborValue::Text(string.to_owned()))
|
.map(|string| CborValue::Text(string.to_owned()))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
@@ -130,7 +160,7 @@ impl MizeMessage {
|
|||||||
pub fn new_give(id: MizeId, data: ItemData, conn_id: u64) -> MizeMessage {
|
pub fn new_give(id: MizeId, data: ItemData, conn_id: u64) -> MizeMessage {
|
||||||
let id_path = id
|
let id_path = id
|
||||||
.path()
|
.path()
|
||||||
.into_iter()
|
.iter()
|
||||||
.map(|string| CborValue::Text(string.to_owned()))
|
.map(|string| CborValue::Text(string.to_owned()))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
@@ -148,7 +178,7 @@ impl MizeMessage {
|
|||||||
pub fn new_update_request(id: MizeId, data: ItemData, conn_id: u64) -> MizeMessage {
|
pub fn new_update_request(id: MizeId, data: ItemData, conn_id: u64) -> MizeMessage {
|
||||||
let id_path = id
|
let id_path = id
|
||||||
.path()
|
.path()
|
||||||
.into_iter()
|
.iter()
|
||||||
.map(|string| CborValue::Text(string.to_owned()))
|
.map(|string| CborValue::Text(string.to_owned()))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
@@ -166,7 +196,7 @@ impl MizeMessage {
|
|||||||
pub fn new_update(id: MizeId, data: ItemData, conn_id: u64) -> MizeMessage {
|
pub fn new_update(id: MizeId, data: ItemData, conn_id: u64) -> MizeMessage {
|
||||||
let id_path = id
|
let id_path = id
|
||||||
.path()
|
.path()
|
||||||
.into_iter()
|
.iter()
|
||||||
.map(|string| CborValue::Text(string.to_owned()))
|
.map(|string| CborValue::Text(string.to_owned()))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
@@ -194,7 +224,7 @@ impl MizeMessage {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
//check if in this map there is a c, otherwise return err
|
//check if in this map there is a cmd, otherwise return err
|
||||||
let val_of_c = {
|
let val_of_c = {
|
||||||
let mut tmp_val = 0;
|
let mut tmp_val = 0;
|
||||||
for (key, val) in msg_as_map {
|
for (key, val) in msg_as_map {
|
||||||
@@ -204,6 +234,8 @@ impl MizeMessage {
|
|||||||
if key_int == &one {
|
if key_int == &one {
|
||||||
if let CborValue::Integer(val_int) = val {
|
if let CborValue::Integer(val_int) = val {
|
||||||
tmp_val = val_int.to_owned().into();
|
tmp_val = val_int.to_owned().into();
|
||||||
|
} else {
|
||||||
|
return Ok(MessageCmd::Custom(ItemData(val.to_owned())));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -214,7 +246,7 @@ impl MizeMessage {
|
|||||||
tmp_val
|
tmp_val
|
||||||
};
|
};
|
||||||
|
|
||||||
// match on value of c
|
// match on value of cmd
|
||||||
let cmd = match val_of_c {
|
let cmd = match val_of_c {
|
||||||
1 => MessageCmd::Get,
|
1 => MessageCmd::Get,
|
||||||
2 => MessageCmd::Update,
|
2 => MessageCmd::Update,
|
||||||
@@ -380,7 +412,7 @@ impl fmt::Display for MizeMessage {
|
|||||||
self.value
|
self.value
|
||||||
.serialize(&mut serde_json::Serializer::pretty(display_writer))
|
.serialize(&mut serde_json::Serializer::pretty(display_writer))
|
||||||
.map_err(|serde_err| std::fmt::Error)
|
.map_err(|serde_err| std::fmt::Error)
|
||||||
.mize_result_msg("serialize error")
|
.mres_msg("serialize error")
|
||||||
.inspect_err(|e| {
|
.inspect_err(|e| {
|
||||||
e.clone().log();
|
e.clone().log();
|
||||||
});
|
});
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
{
|
{
|
||||||
"workspace": [
|
"workspace": [
|
||||||
"./packages/ppc/platform/obsidian",
|
"./apps/obsidian-plugin",
|
||||||
"./packages/marts",
|
"./marts"
|
||||||
"./packages/mize"
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
36
deno.lock
generated
36
deno.lock
generated
@@ -995,7 +995,24 @@
|
|||||||
},
|
},
|
||||||
"workspace": {
|
"workspace": {
|
||||||
"members": {
|
"members": {
|
||||||
"packages/marts": {
|
"apps/obsidian-plugin": {
|
||||||
|
"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"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"marts": {
|
||||||
"packageJson": {
|
"packageJson": {
|
||||||
"dependencies": [
|
"dependencies": [
|
||||||
"npm:@flowershow/remark-wiki-link@^3.4.0",
|
"npm:@flowershow/remark-wiki-link@^3.4.0",
|
||||||
@@ -1012,23 +1029,6 @@
|
|||||||
"npm:unist-util-visit@^5.1.0"
|
"npm:unist-util-visit@^5.1.0"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"packages/ppc/platform/obsidian": {
|
|
||||||
"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"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
0
mize → docs/architecture.md
Executable file → Normal file
0
mize → docs/architecture.md
Executable file → Normal file
57
docs/general-agent-guide.md
Normal file
57
docs/general-agent-guide.md
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
|
||||||
|
## Require clarification and plan approval before making architectural code changes
|
||||||
|
|
||||||
|
Before making any code changes which could be considered to influence the architekture of the application, you must follow this two-step process:
|
||||||
|
|
||||||
|
### Step 1: Ask Clarifying Questions
|
||||||
|
|
||||||
|
- Always ask at least one clarifying question about the user's request
|
||||||
|
- Understand the full scope and context of what they're asking for
|
||||||
|
- Clarify any ambiguous requirements or edge cases
|
||||||
|
- Ask about preferred approaches if multiple solutions exist
|
||||||
|
- Confirm the expected behavior and user experience
|
||||||
|
- Ask the clarifying questions by numbering them, so the user can number their replies.
|
||||||
|
|
||||||
|
### Step 2: Present Implementation Plan
|
||||||
|
|
||||||
|
- After receiving clarification, present a detailed implementation plan
|
||||||
|
- Break down the work into specific, actionable steps
|
||||||
|
- Identify which files will be created, modified, or deleted
|
||||||
|
- Explain the technical approach and any architectural decisions
|
||||||
|
- Highlight any potential risks, trade-offs, or dependencies
|
||||||
|
- Estimate the complexity and scope of changes
|
||||||
|
- **Wait for explicit user approval** before proceeding with any code changes
|
||||||
|
|
||||||
|
### What are considered architectural changes
|
||||||
|
|
||||||
|
**Architectural** (clarify → plan → wait for approval), for example:
|
||||||
|
- New parts, or new cross-part `method` / `opt` surfaces
|
||||||
|
- Wiring one part into another (e.g. UI shell calling `auth.client.login`, soft-notify via `has_part`)
|
||||||
|
- Changes to core loading / `has_part` / part-system semantics
|
||||||
|
- Splitting or merging capabilities across part boundaries
|
||||||
|
|
||||||
|
**Not architectural** (implement directly when the intent is clear), for example:
|
||||||
|
- Layout / CSS / copy tweaks in an existing screen
|
||||||
|
- Adding another display line or button that only uses existing local state or already-exposed methods
|
||||||
|
- Bugfixes that don’t change the part API (e.g. `document::Style` re-render warnings)
|
||||||
|
|
||||||
|
### Approval Requirements
|
||||||
|
|
||||||
|
- User must explicitly approve the plan with words like "yes", "approved", "proceed", "go ahead", or similar
|
||||||
|
- If the user suggests modifications to the plan, incorporate them and seek re-approval
|
||||||
|
- Do not assume silence or ambiguous responses mean approval
|
||||||
|
|
||||||
|
### Exceptions
|
||||||
|
|
||||||
|
- This process may be skipped only for trivial changes like fixing obvious typos or formatting
|
||||||
|
- When in doubt, always follow the full process rather than assuming an exception applies
|
||||||
|
|
||||||
|
### Example Workflow
|
||||||
|
|
||||||
|
1. User: "Add a login form to the app"
|
||||||
|
2. Assistant: "I'd like to clarify a few things about the login form: [questions]"
|
||||||
|
3. User: [provides answers]
|
||||||
|
4. Assistant: "Based on your requirements, here's my implementation plan: [detailed plan]. Does this approach look good to you?"
|
||||||
|
5. User: "Yes, that looks good"
|
||||||
|
6. Assistant: [writes plan to file].
|
||||||
|
7. Assistant: [proceeds with implementation].
|
||||||
615
docs/part-system.md
Normal file
615
docs/part-system.md
Normal file
@@ -0,0 +1,615 @@
|
|||||||
|
# Mize Part System (LLM generated guide)
|
||||||
|
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Core Concepts
|
||||||
|
|
||||||
|
### The MizePart Trait
|
||||||
|
|
||||||
|
All parts implement the `MizePart` trait, which provides:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
pub trait MizePart: MizePartGenerated {
|
||||||
|
fn name(&self) -> &'static str; // Part identifier
|
||||||
|
fn get_mize(&mut self) -> &mut Mize; // Access to the Mize instance
|
||||||
|
fn init(&mut self, mize: &mut Mize) -> MizeResult<()>; // Initialization
|
||||||
|
fn run(&mut self, mize: &mut Mize) -> MizeResult<()>; // Execution
|
||||||
|
fn deps(&self) -> &'static [&'static str]; // Dependencies
|
||||||
|
fn opts(&self, mize: &mut Mize); // Config options
|
||||||
|
// ... async variants
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Part Registration
|
||||||
|
|
||||||
|
Parts register themselves with the Mize instance using:
|
||||||
|
|
||||||
|
- **`mize.register_part(Box::new(MyPart { ... }))`** - For parts with state and methods
|
||||||
|
- **`mize.add_name_only_part("name")`** - For simple parts that only need to be known by name (and have no state)
|
||||||
|
|
||||||
|
### Part Access
|
||||||
|
|
||||||
|
Parts can access other parts using:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
// Get a part by name (returns a guard that ensures the part is returned)
|
||||||
|
let mut part = mize.get_part("part_name")?;
|
||||||
|
|
||||||
|
// Get a typed part (for parts with structs)
|
||||||
|
let mut typed_part = mize.get_part_native::<OtherPart>("other_part")?;
|
||||||
|
```
|
||||||
|
|
||||||
|
**Important**: Users must ensure that dependent parts are enabled before the parts that depend on them. There's no automatic dependency resolution.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Current Implementation (Compiled-in Parts)
|
||||||
|
|
||||||
|
Currently, all parts are compiled into the `marts` crate and registered via the `compile_in_parts!` macro in the application's `main.rs`.
|
||||||
|
|
||||||
|
### Example: `apps/os/src/main.rs`
|
||||||
|
|
||||||
|
```rust
|
||||||
|
compile_in_parts! {
|
||||||
|
mize,
|
||||||
|
"cli": marts::cli,
|
||||||
|
"auth": marts::part_client_auth,
|
||||||
|
"spacetime": marts::spacetime,
|
||||||
|
"ppc.server": marts::ppc::server::part_ppc_server,
|
||||||
|
"c2vi": marts::part_c2vi,
|
||||||
|
"sci": marts::sci::part_sci,
|
||||||
|
"sci.timetrack": marts::sci::timetrack::part_timetrack,
|
||||||
|
"sci.inbox": marts::sci::inbox::part_sci_inbox,
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
Each entry maps a part name to a function that registers the part with the Mize instance.
|
||||||
|
|
||||||
|
### How It Works
|
||||||
|
|
||||||
|
1. The macro reads the `enable` config option (a list of part names)
|
||||||
|
2. For each enabled part, it calls the corresponding registration function
|
||||||
|
3. Each registration function creates the part and calls `mize.register_part()`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Creating a New Part
|
||||||
|
|
||||||
|
### Option 1: Simple Name-Only Part
|
||||||
|
|
||||||
|
For parts that don't need state or methods:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
// In your part module
|
||||||
|
pub fn part_my_simple_part(mize: &mut Mize) -> MizeResult<()> {
|
||||||
|
// Just register the name
|
||||||
|
mize.add_name_only_part("my_simple_part");
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Option 2: Part with State and Methods (Using `mize_part` Macro)
|
||||||
|
|
||||||
|
For parts that need to store state and expose methods:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
use mize::{mize_part, Mize, MizePart, MizePartCreate, MizeResult};
|
||||||
|
|
||||||
|
#[mize_part] // or #[mize_part("custom_name")] for custom name
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct MyPart {
|
||||||
|
mize: Mize, // Required field
|
||||||
|
counter: u32, // Your custom state
|
||||||
|
api_key: String, // More state
|
||||||
|
}
|
||||||
|
|
||||||
|
// Implement MizePartCreate for parts that need custom initialization
|
||||||
|
impl MizePartCreate for MyPart {
|
||||||
|
// Optional: override create_generated for custom initialization
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MizePart for MyPart {
|
||||||
|
// Optional: override name if not using the struct name
|
||||||
|
fn name(&self) -> &'static str {
|
||||||
|
"my_part" // Default would be "mypart" from struct name
|
||||||
|
}
|
||||||
|
|
||||||
|
// Optional: define config options this part needs
|
||||||
|
fn opts(&self, mize: &mut Mize) {
|
||||||
|
mize.new_opt("my_part.api_key");
|
||||||
|
mize.new_opt("my_part.counter_initial");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Optional: initialization logic
|
||||||
|
fn init(&mut self, mize: &mut Mize) -> MizeResult<()> {
|
||||||
|
self.counter = mize.get_config("my_part.counter_initial")?.value_u32()?;
|
||||||
|
self.api_key = mize.get_config("my_part.api_key")?.value_string()?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Optional: define dependencies on other parts
|
||||||
|
fn deps(&self) -> &'static [&'static str] {
|
||||||
|
&["auth"] // This part requires "auth" to be loaded first
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The registration function
|
||||||
|
pub fn part_my_part(mize: &mut Mize) -> MizeResult<()> {
|
||||||
|
mize.register_part(Box::new(MyPart {
|
||||||
|
mize: mize.clone(),
|
||||||
|
counter: 0,
|
||||||
|
api_key: String::new(),
|
||||||
|
}))?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
// You can add methods to your part struct
|
||||||
|
impl MyPart {
|
||||||
|
pub fn increment(&mut self) -> u32 {
|
||||||
|
self.counter += 1;
|
||||||
|
self.counter
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_api_key(&self) -> &str {
|
||||||
|
&self.api_key
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Using the Part from Another Part
|
||||||
|
|
||||||
|
```rust
|
||||||
|
pub fn part_another_part(mize: &mut Mize) -> MizeResult<()> {
|
||||||
|
// Get the MyPart instance
|
||||||
|
let mut my_part = mize.get_part_native::<MyPart>("my_part")?;
|
||||||
|
|
||||||
|
// Call its methods
|
||||||
|
let count = my_part.increment();
|
||||||
|
let api_key = my_part.get_api_key();
|
||||||
|
|
||||||
|
// ... your logic
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## The `mize_part` Macro
|
||||||
|
|
||||||
|
The `#[mize_part]` procedural macro simplifies part creation by automatically implementing the required traits.
|
||||||
|
|
||||||
|
### What It Generates
|
||||||
|
|
||||||
|
For a struct like:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
#[mize_part]
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct MyPart {
|
||||||
|
mize: Mize,
|
||||||
|
// ... your fields
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The macro generates:
|
||||||
|
|
||||||
|
1. **`MizePartGenerated` implementation** - Provides default implementations for the generated trait methods
|
||||||
|
2. **`MizePartCreateGenerated` implementation** - Provides a default `create_generated` method
|
||||||
|
3. **`MizePartCreateThisSucks` implementation** - Compatibility trait
|
||||||
|
4. **Extern "C" function** - Named `make_mize_part_<name>` for dynamic loading
|
||||||
|
|
||||||
|
### Custom Part Name
|
||||||
|
|
||||||
|
```rust
|
||||||
|
#[mize_part("custom_name")]
|
||||||
|
pub struct MyPart {
|
||||||
|
mize: Mize,
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This will use "custom_name" instead of the derived "mypart".
|
||||||
|
|
||||||
|
### Requirements
|
||||||
|
|
||||||
|
- The struct **must** have a field named `mize: Mize`
|
||||||
|
- The struct should derive `Default` or implement it
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
### Defining Config Options
|
||||||
|
(about to be deprecad, does nothing)
|
||||||
|
|
||||||
|
Parts declare what config options they need in their `opts()` method:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
impl MizePart for MyPart {
|
||||||
|
fn opts(&self, mize: &mut Mize) {
|
||||||
|
mize.new_opt("my_part.api_url");
|
||||||
|
mize.new_opt("my_part.timeout");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Using Config Options
|
||||||
|
|
||||||
|
Parts retrieve config using `mize.get_config()`:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
fn init(&mut self, mize: &mut Mize) -> MizeResult<()> {
|
||||||
|
let api_url = mize.get_config("my_part.api_url")?.value_string()?;
|
||||||
|
let timeout = mize.get_config("my_part.timeout")?.value_u64()?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### User Configuration
|
||||||
|
|
||||||
|
Users configure parts via TOML files or environment variables. Example `config.toml`:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[my_part]
|
||||||
|
api_url = "https://api.example.com"
|
||||||
|
timeout = 30
|
||||||
|
|
||||||
|
[enable]
|
||||||
|
# List of parts to enable
|
||||||
|
my_part = true
|
||||||
|
auth = true
|
||||||
|
spacetime = true
|
||||||
|
```
|
||||||
|
|
||||||
|
Or via command line:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mize --opt my_part.api_url=https://api.example.com run
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Part Dependencies
|
||||||
|
|
||||||
|
### Declaring Dependencies
|
||||||
|
(about to be deprecad, does nothing)
|
||||||
|
|
||||||
|
Parts declare their dependencies in the `deps()` method:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
impl MizePart for MyPart {
|
||||||
|
fn deps(&self) -> &'static [&'static str] {
|
||||||
|
&["auth", "spacetime"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Note**: This is currently informational only. Users must manually ensure dependencies are enabled.
|
||||||
|
|
||||||
|
### Accessing Dependencies
|
||||||
|
|
||||||
|
Use `get_part_native()` to access other parts:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
fn run(&mut self, mize: &mut Mize) -> MizeResult<()> {
|
||||||
|
// Get the auth part
|
||||||
|
let mut auth = mize.get_part_native::<ClientAuth>("client_auth")?;
|
||||||
|
|
||||||
|
// Use it
|
||||||
|
let token = auth.id_token()?;
|
||||||
|
|
||||||
|
// Get spacetime
|
||||||
|
let mut spacetime = mize.get_part_native::<SpacetimePart>("spacetime")?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Future Vision: Separate Crates
|
||||||
|
|
||||||
|
The goal is to move each part to its own crate following the naming convention **`mize-part-<name>`**. when creating a part make a new Cargo project like this already.
|
||||||
|
|
||||||
|
### Structure of a Part Crate
|
||||||
|
|
||||||
|
```
|
||||||
|
mize-part-discord/
|
||||||
|
├── Cargo.toml
|
||||||
|
└── src/
|
||||||
|
└── lib.rs
|
||||||
|
```
|
||||||
|
|
||||||
|
### Example: `mize-part-discord/Cargo.toml`
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[package]
|
||||||
|
name = "mize-part-discord"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
crate-type = ["cdylib", "rlib"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
mize = { path = "../mize" }
|
||||||
|
serenity = "0.12"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Example: `mize-part-discord/src/lib.rs`
|
||||||
|
|
||||||
|
```rust
|
||||||
|
use mize::{mize_part, Mize, MizePart, MizePartCreate, MizeResult};
|
||||||
|
use serenity::Client;
|
||||||
|
|
||||||
|
#[mize_part]
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct DiscordPart {
|
||||||
|
mize: Mize,
|
||||||
|
client: Option<Client>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MizePartCreate for DiscordPart {}
|
||||||
|
|
||||||
|
impl MizePart for DiscordPart {
|
||||||
|
fn name(&self) -> &'static str {
|
||||||
|
"discord"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn opts(&self, mize: &mut Mize) {
|
||||||
|
mize.new_opt("discord.token");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn init(&mut self, mize: &mut Mize) -> MizeResult<()> {
|
||||||
|
let token = mize.get_config("discord.token")?.value_string()?;
|
||||||
|
// Initialize serenity client...
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The registration function - THIS IS WHAT MATTERS
|
||||||
|
pub fn part_discord(mize: &mut Mize) -> MizeResult<()> {
|
||||||
|
mize.register_part(Box::new(DiscordPart {
|
||||||
|
mize: mize.clone(),
|
||||||
|
client: None,
|
||||||
|
}))?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Using Separate Part Crates
|
||||||
|
|
||||||
|
In your application's `Cargo.toml`:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[dependencies]
|
||||||
|
mize = { path = "../mize" }
|
||||||
|
mize-part-discord = { path = "../mize-part-discord" }
|
||||||
|
mize-part-auth = { path = "../mize-part-auth" }
|
||||||
|
```
|
||||||
|
|
||||||
|
In your application's `main.rs`:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
compile_in_parts! {
|
||||||
|
mize,
|
||||||
|
"discord": mize_part_discord::part_discord,
|
||||||
|
"auth": mize_part_auth::part_auth,
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
## Complete Example: Creating a New Part
|
||||||
|
|
||||||
|
Let's create a complete example of a new part called `mize-part-greeting`.
|
||||||
|
|
||||||
|
### Step 1: Create the Crate
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cargo new --lib mize-part-greeting
|
||||||
|
cd mize-part-greeting
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 2: Edit `Cargo.toml`
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[package]
|
||||||
|
name = "mize-part-greeting"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
crate-type = ["cdylib", "rlib"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
mize = { path = "../mize" }
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 3: Create the Part (`src/lib.rs`)
|
||||||
|
|
||||||
|
```rust
|
||||||
|
use mize::{mize_part, Mize, MizePart, MizePartCreate, MizeResult};
|
||||||
|
|
||||||
|
/// A simple greeting part that stores a name and can generate greetings
|
||||||
|
#[mize_part]
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct GreetingPart {
|
||||||
|
mize: Mize,
|
||||||
|
name: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MizePartCreate for GreetingPart {}
|
||||||
|
|
||||||
|
impl MizePart for GreetingPart {
|
||||||
|
fn name(&self) -> &'static str {
|
||||||
|
"greeting"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn opts(&self, mize: &mut Mize) {
|
||||||
|
mize.new_opt("greeting.name");
|
||||||
|
mize.new_opt("greeting.formal");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn init(&mut self, mize: &mut Mize) -> MizeResult<()> {
|
||||||
|
self.name = mize
|
||||||
|
.get_config("greeting.name")?
|
||||||
|
.value_string()
|
||||||
|
.unwrap_or_else(|_| "World".to_string());
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GreetingPart {
|
||||||
|
/// Generate a greeting message
|
||||||
|
pub fn greet(&self) -> String {
|
||||||
|
let formal = self
|
||||||
|
.mize
|
||||||
|
.get_config("greeting.formal")
|
||||||
|
.map(|v| v.value_bool().unwrap_or(false))
|
||||||
|
.unwrap_or(false);
|
||||||
|
|
||||||
|
if formal {
|
||||||
|
format!("Good day, {}!", self.name)
|
||||||
|
} else {
|
||||||
|
format!("Hello, {}!", self.name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the name
|
||||||
|
pub fn set_name(&mut self, name: String) {
|
||||||
|
self.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the current name
|
||||||
|
pub fn get_name(&self) -> &str {
|
||||||
|
&self.name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Registration function - this is what the compile_in_parts! macro will call
|
||||||
|
pub fn part_greeting(mize: &mut Mize) -> MizeResult<()> {
|
||||||
|
mize.register_part(Box::new(GreetingPart {
|
||||||
|
mize: mize.clone(),
|
||||||
|
name: String::new(),
|
||||||
|
}))?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 4: Add to Your Application
|
||||||
|
|
||||||
|
In your application's `Cargo.toml`:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[dependencies]
|
||||||
|
mize = { path = "../mize" }
|
||||||
|
mize-part-greeting = { path = "../mize-part-greeting" }
|
||||||
|
```
|
||||||
|
|
||||||
|
In your application's `main.rs`:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
compile_in_parts! {
|
||||||
|
mize,
|
||||||
|
"greeting": mize_part_greeting::part_greeting,
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 5: Use the Part
|
||||||
|
|
||||||
|
```rust
|
||||||
|
// In another part or in your CLI
|
||||||
|
let mut greeting = mize.get_part_native::<GreetingPart>("greeting")?;
|
||||||
|
println!("{}", greeting.greet());
|
||||||
|
|
||||||
|
// Change the name
|
||||||
|
greeting.set_name("Alice".to_string());
|
||||||
|
println!("{}", greeting.greet());
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 6: Configure It
|
||||||
|
|
||||||
|
Create a `config.toml`:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[greeting]
|
||||||
|
name = "Bob"
|
||||||
|
formal = true
|
||||||
|
|
||||||
|
[enable]
|
||||||
|
greeting = true
|
||||||
|
```
|
||||||
|
|
||||||
|
Run with:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mize --config config.toml run
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Best Practices
|
||||||
|
|
||||||
|
1. **Naming**: Use `mize-part-<name>` for crate names and `part_<name>` for registration functions
|
||||||
|
2. **State**: Store part-specific state in the part struct
|
||||||
|
3. **Dependencies**: Declare dependencies in `deps()` and document them
|
||||||
|
4. **Config**: Declare all config options in `opts()`
|
||||||
|
5. **Error Handling**: Use `MizeResult` and `mize_err!` for errors
|
||||||
|
6. **Documentation**: Document your part's purpose, config options, and methods
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
|
||||||
|
- Parts are modular units of functionality in Mize
|
||||||
|
- Each part implements the `MizePart` trait
|
||||||
|
- Use `#[mize_part]` macro for parts with state and methods
|
||||||
|
- Register parts with `mize.register_part()` or `mize.add_name_only_part()`
|
||||||
|
- Parts access config via `mize.get_config()`
|
||||||
|
- Parts access other parts via `mize.get_part_native::<T>()`
|
||||||
|
- Users enable parts via the `enable` config option
|
||||||
|
- Future: Each part will be its own crate named `mize-part-<name>`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Quick Reference
|
||||||
|
|
||||||
|
### Common Patterns
|
||||||
|
|
||||||
|
**Register a part:**
|
||||||
|
```rust
|
||||||
|
mize.register_part(Box::new(MyPart { mize: mize.clone(), ... }))?;
|
||||||
|
```
|
||||||
|
|
||||||
|
**Get a part:**
|
||||||
|
```rust
|
||||||
|
let mut part = mize.get_part_native::<MyPart>("my_part")?;
|
||||||
|
```
|
||||||
|
|
||||||
|
**Get config:**
|
||||||
|
```rust
|
||||||
|
let value = mize.get_config("my_part.setting")?.value_string()?;
|
||||||
|
```
|
||||||
|
|
||||||
|
**Declare config option:**
|
||||||
|
```rust
|
||||||
|
mize.new_opt("my_part.setting");
|
||||||
|
```
|
||||||
|
|
||||||
|
**Define a part struct:**
|
||||||
|
```rust
|
||||||
|
#[mize_part]
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct MyPart {
|
||||||
|
mize: Mize,
|
||||||
|
// your fields...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Registration function:**
|
||||||
|
```rust
|
||||||
|
pub fn part_my_part(mize: &mut Mize) -> MizeResult<()> {
|
||||||
|
mize.register_part(Box::new(MyPart { mize: mize.clone(), ... }))?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
```
|
||||||
18
docs/plans/2026-11-03-dist-publish.md
Normal file
18
docs/plans/2026-11-03-dist-publish.md
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
# Plan: dist publish + dist.mize.works (approved)
|
||||||
|
|
||||||
|
## Config
|
||||||
|
- `parts.workspaces`: list of dirs; each subdir is a part to build/upload
|
||||||
|
- `parts.dist.cd`: bool; when true artifact uses `-cd` instead of `-{version}`
|
||||||
|
|
||||||
|
## Publish (`mize.dev`)
|
||||||
|
- After build: pack each part `.so` under `lib/`, special part `mize` gets `libmize.so` + `bin/mize`
|
||||||
|
- Hash via `get_part_hash` with modName, system (target), mize_version
|
||||||
|
- Filename: `{hash}-{id}-{version}.tar.gz` or `{hash}-{id}-cd.tar.gz`
|
||||||
|
- rsync to `ocib:host/mize-dist/`
|
||||||
|
|
||||||
|
## fetch_part
|
||||||
|
- Base URL `https://dist.mize.works/`
|
||||||
|
- Same naming; ensure mize_version in selector
|
||||||
|
|
||||||
|
## ocib
|
||||||
|
- `~/host/docker-compose.yaml`: Traefik + kugland/darkhttpd serving `mize-dist` for `dist.mize.works`
|
||||||
14
docs/plans/2026-11-04-load-part-at-instances.md
Normal file
14
docs/plans/2026-11-04-load-part-at-instances.md
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
# load_part_at instances (priv.spacetime / ppc.spacetime)
|
||||||
|
|
||||||
|
Approved 2026-11-04.
|
||||||
|
|
||||||
|
## Goal
|
||||||
|
Instantiate the same `spacetime` `.so` under different part ids (`priv.spacetime`, `ppc.spacetime`), each with its own config namespace. Dependents use `Spacetime::get("priv.spacetime")` etc.
|
||||||
|
|
||||||
|
## Changes
|
||||||
|
1. Platform `load_part(part_name, path, at_id)` via `get_global_mize()`; save/restore `cur_eval_part`.
|
||||||
|
2. Rename `Mize::load_part_at` → `load_part_from_path`.
|
||||||
|
3. `config::load_part_at(at_id, part_name)` — no-op in generate mode.
|
||||||
|
4. Codegen: special SpacetimeDB client gen when `gen` contains `"spacetime"`, module at `{cwd}/spacetime` if present.
|
||||||
|
5. `sci`: `gen += spacetime`, `load_part_at("priv.spacetime", "spacetime")`.
|
||||||
|
6. New `mize/parts/ppc`: loads `ppc.spacetime`.
|
||||||
6
flake.lock
generated
6
flake.lock
generated
@@ -72,11 +72,11 @@
|
|||||||
},
|
},
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1774709303,
|
"lastModified": 1782467914,
|
||||||
"narHash": "sha256-D3Q07BbIA2KnTcSXIqqu9P586uWxN74zNoCH3h2ESHg=",
|
"narHash": "sha256-pGvFkM8N0xEkIIXDe5YYfbEAvHrk4IxBrjB/x8OomhE=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "8110df5ad7abf5d4c0f6fb0f8f978390e77f9685",
|
"rev": "e73de5be04e0eff4190a1432b946d469c794e7b4",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
|||||||
439
flake.nix
439
flake.nix
@@ -1,6 +1,5 @@
|
|||||||
{
|
{
|
||||||
inputs = {
|
inputs = {
|
||||||
# nixpkgs 35.11 still contains rust 1.73
|
|
||||||
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
||||||
|
|
||||||
fenix = {
|
fenix = {
|
||||||
@@ -18,7 +17,7 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
mize_modules = {
|
mize_modules = {
|
||||||
url = "github:c2vi/mize-modules?submodules=1";
|
url = "github:c2vi/mize-modules";
|
||||||
flake = false;
|
flake = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -29,213 +28,148 @@
|
|||||||
############################## LET BINDINGS ##############################
|
############################## LET BINDINGS ##############################
|
||||||
let
|
let
|
||||||
pkgs = nixpkgs.legacyPackages.${system};
|
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;
|
|
||||||
|
|
||||||
|
osToolchain = fenix.packages.${system}.latest.toolchain;
|
||||||
osCrane = (crane.mkLib pkgs).overrideToolchain osToolchain;
|
osCrane = (crane.mkLib pkgs).overrideToolchain osToolchain;
|
||||||
|
|
||||||
#winToolchain = fenix.packages.${system}.combine [
|
# mize build time config
|
||||||
#fenix.packages.${system}.targets.x86_64-pc-windows-gnu.latest.toolchain
|
mizeBuildConfig = {
|
||||||
#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";
|
namespace = "mize.buildtime.ns";
|
||||||
module_url = "c2vi.dev";
|
module_url = "parts.mize.works";
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#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 {
|
in {
|
||||||
############################## PACKAGES ##############################
|
############################## PACKAGES ##############################
|
||||||
|
|
||||||
packages = rec {
|
packages = rec {
|
||||||
|
|
||||||
inherit osCrane;
|
fileSetForCrate =
|
||||||
#test = mizeLib.buildMizeForSystem "wasm32-unknown-none-unknown";
|
crate:
|
||||||
test = mizeLib.buildMizeForSystem "aarch64-linux";
|
pkgs.lib.fileset.toSource {
|
||||||
#test = mizeLib.buildMizeForSystem "x86_64-pc-windows-gnu";
|
root = ./.;
|
||||||
|
fileset = pkgs.lib.fileset.unions [
|
||||||
mizeFor = let
|
./Cargo.toml
|
||||||
mizes = map mizeLib.buildMizeForSystem systems;
|
./Cargo.lock
|
||||||
in builtins.listToAttrs ( map ( mize: { name = mize.system.name; value = mize; } ) mizes );
|
(osCrane.fileset.commonCargoSources ./packages/marts)
|
||||||
|
(osCrane.fileset.commonCargoSources ./packages/mize)
|
||||||
dist = mizeLib.dist systems;
|
(osCrane.fileset.commonCargoSources ./packages/ppc/spacetimedb)
|
||||||
|
(osCrane.fileset.commonCargoSources ./packages/mme)
|
||||||
random = mizeLib.random systems;
|
(osCrane.fileset.commonCargoSources ./packages/vic)
|
||||||
|
(osCrane.fileset.commonCargoSources ./packages/ac_mize_macros)
|
||||||
inherit mizeLib;
|
(osCrane.fileset.commonCargoSources crate)
|
||||||
|
];
|
||||||
|
|
||||||
#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 {
|
ppc = osCrane.buildPackage {
|
||||||
src = "${self}";
|
src = fileSetForCrate ./packages/ppc;
|
||||||
cargoExtraArgs = "--bin mize --features target-os";
|
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
|
||||||
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
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 ##############################
|
############################## DEV SHELLS ##############################
|
||||||
devShells = {
|
devShells = {
|
||||||
|
|
||||||
modules = mizeLib.moduleShells system;
|
# shell without spacetime and other dev commands. just all needed to build mize or ppc
|
||||||
|
buildShell = pkgs.mkShell {
|
||||||
|
|
||||||
modulesCross = pkgs.lib.genAttrs systems (system: mizeLib.moduleShells system);
|
MIZE_BUILD_CONFIG = builtins.toJSON mizeBuildConfig;
|
||||||
|
|
||||||
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;
|
|
||||||
default = pkgs.mkShell {
|
|
||||||
|
|
||||||
MIZE_BUILD_CONFIG = let
|
|
||||||
settingsFormat = pkgs.formats.toml { };
|
|
||||||
in settingsFormat.generate "mize-build-config.toml" {
|
|
||||||
config = {
|
|
||||||
namespace = "mize.buildtime.ns";
|
|
||||||
module_url = "c2vi.dev";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
shellHook = ''
|
shellHook = ''
|
||||||
mize_project_root=$(${pkgs.lib.getExe pkgs.git} rev-parse --show-toplevel)
|
mize_project_root=$(${pkgs.lib.getExe pkgs.git} rev-parse --show-toplevel)
|
||||||
export MIZE_CONFIG_FILES=$mize_project_root/mize_config.toml
|
export MIZE_CONFIG_FILES=$mize_project_root/mize_config.toml
|
||||||
'';
|
'';
|
||||||
#_shell_type = "rust";
|
|
||||||
nativeBuildInputs = [
|
nativeBuildInputs = [
|
||||||
pkgs.wasm-pack
|
pkgs.wasm-pack
|
||||||
pkgs.dioxus-cli
|
|
||||||
pkgs.spacetimedb
|
|
||||||
pkgs.deno
|
|
||||||
pkgs.pkg-config
|
pkgs.pkg-config
|
||||||
pkgs.gobject-introspection
|
pkgs.gobject-introspection
|
||||||
pkgs.gobject-introspection.dev
|
pkgs.gobject-introspection.dev
|
||||||
|
pkgs.deno
|
||||||
|
|
||||||
# thanks to: https://github.com/NixOS/nixpkgs/pull/496279/changes
|
# thanks to: https://github.com/NixOS/nixpkgs/pull/496279/changes
|
||||||
(pkgs.buildWasmBindgenCli rec {
|
(pkgs.buildWasmBindgenCli rec {
|
||||||
@@ -292,12 +226,157 @@ in {
|
|||||||
];
|
];
|
||||||
|
|
||||||
};
|
};
|
||||||
|
default = pkgs.mkShell rec {
|
||||||
|
|
||||||
|
MIZE_BUILD_CONFIG = builtins.toJSON mizeBuildConfig;
|
||||||
|
shellHook = ''
|
||||||
|
mize_project_root=$(${pkgs.lib.getExe pkgs.git} rev-parse --show-toplevel)
|
||||||
|
'';
|
||||||
|
#_shell_type = "rust";
|
||||||
|
nativeBuildInputs = [
|
||||||
|
pkgs.wasm-pack
|
||||||
|
pkgs.dioxus-cli
|
||||||
|
pkgs.spacetimedb
|
||||||
|
pkgs.deno
|
||||||
|
pkgs.pkg-config
|
||||||
|
pkgs.gobject-introspection
|
||||||
|
pkgs.gobject-introspection.dev
|
||||||
|
pkgs.systemd
|
||||||
|
pkgs.rsync
|
||||||
|
pkgs.openssh
|
||||||
|
pkgs.cmake
|
||||||
|
|
||||||
|
# to build ../grid
|
||||||
|
pkgs.rustPlatform.bindgenHook
|
||||||
|
pkgs.pipewire.dev
|
||||||
|
pkgs.alsa-lib.dev
|
||||||
|
|
||||||
|
# thanks to: https://github.com/NixOS/nixpkgs/pull/496279/changes
|
||||||
|
(pkgs.buildWasmBindgenCli rec {
|
||||||
|
src = pkgs.fetchCrate {
|
||||||
|
pname = "wasm-bindgen-cli";
|
||||||
|
version = "0.2.114";
|
||||||
|
hash = "sha256-xrCym+rFY6EUQFWyWl6OPA+LtftpUAE5pIaElAIVqW0=";
|
||||||
};
|
};
|
||||||
|
|
||||||
}) // {
|
cargoDeps = pkgs.rustPlatform.fetchCargoVendor {
|
||||||
|
inherit src;
|
||||||
|
inherit (src) pname version;
|
||||||
|
hash = "sha256-Z8+dUXPQq7S+Q7DWNr2Y9d8GMuEdSnq00quUR0wDNPM=";
|
||||||
|
};
|
||||||
|
})
|
||||||
|
(fenix.packages.${system}.combine [
|
||||||
|
fenix.packages.${system}.latest.toolchain
|
||||||
|
fenix.packages.${system}.targets.wasm32-unknown-unknown.latest.toolchain
|
||||||
|
])
|
||||||
|
];
|
||||||
|
|
||||||
############################## SOME GLOBAL OUTPUTS ##############################
|
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;
|
inherit inputs self;
|
||||||
rustPkgs = import nixpkgs { overlays = [rust-overlay.overlays.default]; system = "x86_64-linux"; };
|
|
||||||
|
############################## 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;
|
||||||
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
445
lib.nix
445
lib.nix
@@ -1,445 +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 // {
|
|
||||||
shellHook = ''
|
|
||||||
mize_project_root=$(${lib.getExe pkgs.git} rev-parse --show-toplevel)
|
|
||||||
export MIZE_CONFIG_FILES=$mize_project_root/mize_config.toml
|
|
||||||
'';
|
|
||||||
#_shell_type = "rust";
|
|
||||||
nativeBuildInputs = attrs.nativeBuildInputs or [] ++ [
|
|
||||||
pkgs.wasm-pack
|
|
||||||
pkgs.dioxus-cli
|
|
||||||
pkgs.spacetimedb
|
|
||||||
pkgs.deno
|
|
||||||
pkgs.pkg-config
|
|
||||||
pkgs.gobject-introspection
|
|
||||||
pkgs.gobject-introspection.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."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 = 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
|
|
||||||
];
|
|
||||||
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -9,3 +9,5 @@ proc-macro = true
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
quote = "1.0.44"
|
quote = "1.0.44"
|
||||||
syn = "2.0.114"
|
syn = "2.0.114"
|
||||||
|
proc-macro2 = "1.0"
|
||||||
|
ciborium = "0.2"
|
||||||
554
macros/src/lib.rs
Normal file
554
macros/src/lib.rs
Normal file
@@ -0,0 +1,554 @@
|
|||||||
|
use proc_macro::TokenStream;
|
||||||
|
use quote::quote;
|
||||||
|
use syn::{self, parse::Parse, parse::ParseStream, Ident, LitStr, Token, Type, TypePath};
|
||||||
|
use syn::punctuated::Punctuated;
|
||||||
|
use syn::token;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//////////////////////////////////// include_native! macro ///////////////////////////////////////
|
||||||
|
/// A procedural macro to register methods and associated functions of a foreign type with mize.
|
||||||
|
///
|
||||||
|
/// Usage 1: Simplified syntax with no-argument methods
|
||||||
|
/// ```ignore
|
||||||
|
/// include_native!("Command", clap::Command, [get_name, is_empty]);
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// Usage 2: With argument specifications
|
||||||
|
/// ```ignore
|
||||||
|
/// include_native!("Command", clap::Command, [
|
||||||
|
/// arg(arg: clap::Arg),
|
||||||
|
/// subcommand(cmd: clap::Command)
|
||||||
|
/// ]);
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// Usage 3: Block syntax separating associated functions and methods
|
||||||
|
/// ```ignore
|
||||||
|
/// include_native!("Command", clap::Command, {
|
||||||
|
/// associated: [new(name: String)],
|
||||||
|
/// methods: [arg(arg: clap::Arg), subcommand(cmd: clap::Command)]
|
||||||
|
/// });
|
||||||
|
/// ```
|
||||||
|
|
||||||
|
/// A function specification: name or name(arg1: Type1, arg2: Type2, ...)
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
struct FuncSpec {
|
||||||
|
name: Ident,
|
||||||
|
args: Vec<(Ident, Type)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Parse for FuncSpec {
|
||||||
|
fn parse(input: ParseStream) -> syn::Result<Self> {
|
||||||
|
let name: Ident = input.parse()?;
|
||||||
|
let mut args = Vec::new();
|
||||||
|
|
||||||
|
// Check for parentheses with arguments
|
||||||
|
if input.peek(syn::token::Paren) {
|
||||||
|
let content;
|
||||||
|
syn::parenthesized!(content in input);
|
||||||
|
|
||||||
|
// Parse comma-separated arg: Type pairs
|
||||||
|
while !content.is_empty() {
|
||||||
|
let arg_name: Ident = content.parse()?;
|
||||||
|
content.parse::<Token![:]>()?;
|
||||||
|
let arg_type: Type = content.parse()?;
|
||||||
|
args.push((arg_name, arg_type));
|
||||||
|
|
||||||
|
if !content.is_empty() {
|
||||||
|
content.parse::<Token![,]>()?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(FuncSpec { name, args })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Helper struct to parse a list of function specifications
|
||||||
|
struct FuncSpecList(Vec<FuncSpec>);
|
||||||
|
|
||||||
|
impl Parse for FuncSpecList {
|
||||||
|
fn parse(input: ParseStream) -> syn::Result<Self> {
|
||||||
|
let punctuated = Punctuated::<FuncSpec, Token![,]>::parse_terminated(input)?;
|
||||||
|
Ok(FuncSpecList(punctuated.into_iter().collect()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Input for the simplified syntax: include_native!("name", Type, [method1, method2, ...])
|
||||||
|
struct IncludeNativeSimple {
|
||||||
|
name: LitStr,
|
||||||
|
type_path: TypePath,
|
||||||
|
methods: Vec<FuncSpec>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Parse for IncludeNativeSimple {
|
||||||
|
fn parse(input: ParseStream) -> syn::Result<Self> {
|
||||||
|
let name = input.parse()?;
|
||||||
|
input.parse::<Token![,]>()?;
|
||||||
|
let type_path = input.parse()?;
|
||||||
|
input.parse::<Token![,]>()?;
|
||||||
|
|
||||||
|
let content;
|
||||||
|
syn::bracketed!(content in input);
|
||||||
|
let methods = content.parse::<FuncSpecList>()?.0;
|
||||||
|
|
||||||
|
Ok(IncludeNativeSimple { name, type_path, methods })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Struct for parsing the block syntax with associated and methods
|
||||||
|
struct IncludeNativeBlock {
|
||||||
|
name: LitStr,
|
||||||
|
type_path: TypePath,
|
||||||
|
associated: Vec<FuncSpec>,
|
||||||
|
methods: Vec<FuncSpec>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Parse for IncludeNativeBlock {
|
||||||
|
fn parse(input: ParseStream) -> syn::Result<Self> {
|
||||||
|
let name = input.parse()?;
|
||||||
|
input.parse::<Token![,]>()?;
|
||||||
|
let type_path = input.parse()?;
|
||||||
|
input.parse::<Token![,]>()?;
|
||||||
|
|
||||||
|
let content;
|
||||||
|
syn::braced!(content in input);
|
||||||
|
|
||||||
|
let mut associated = Vec::new();
|
||||||
|
let mut methods = Vec::new();
|
||||||
|
|
||||||
|
// Parse the content looking for associated: [...] and methods: [...]
|
||||||
|
// We'll collect all tokens first and then parse
|
||||||
|
let tokens: Vec<proc_macro2::TokenTree> = content.parse::<proc_macro2::TokenStream>()?.into_iter().collect();
|
||||||
|
let mut i = 0;
|
||||||
|
while i < tokens.len() {
|
||||||
|
if let proc_macro2::TokenTree::Ident(id) = &tokens[i] {
|
||||||
|
let id_str = id.to_string();
|
||||||
|
if id_str == "associated" {
|
||||||
|
// Find colon
|
||||||
|
let mut j = i + 1;
|
||||||
|
while j < tokens.len() {
|
||||||
|
if let proc_macro2::TokenTree::Punct(p) = &tokens[j] {
|
||||||
|
if p.as_char() == ':' {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
j += 1;
|
||||||
|
}
|
||||||
|
j += 1; // Skip colon
|
||||||
|
|
||||||
|
// Find bracket group
|
||||||
|
while j < tokens.len() {
|
||||||
|
if let proc_macro2::TokenTree::Group(g) = &tokens[j] {
|
||||||
|
if g.delimiter() == proc_macro2::Delimiter::Bracket {
|
||||||
|
associated = syn::parse2::<FuncSpecList>(g.stream())
|
||||||
|
.map(|l| l.0)
|
||||||
|
.unwrap_or_default();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
j += 1;
|
||||||
|
}
|
||||||
|
i = j + 1;
|
||||||
|
} else if id_str == "methods" {
|
||||||
|
// Find colon
|
||||||
|
let mut j = i + 1;
|
||||||
|
while j < tokens.len() {
|
||||||
|
if let proc_macro2::TokenTree::Punct(p) = &tokens[j] {
|
||||||
|
if p.as_char() == ':' {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
j += 1;
|
||||||
|
}
|
||||||
|
j += 1; // Skip colon
|
||||||
|
|
||||||
|
// Find bracket group
|
||||||
|
while j < tokens.len() {
|
||||||
|
if let proc_macro2::TokenTree::Group(g) = &tokens[j] {
|
||||||
|
if g.delimiter() == proc_macro2::Delimiter::Bracket {
|
||||||
|
methods = syn::parse2::<FuncSpecList>(g.stream())
|
||||||
|
.map(|l| l.0)
|
||||||
|
.unwrap_or_default();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
j += 1;
|
||||||
|
}
|
||||||
|
i = j + 1;
|
||||||
|
} else {
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(IncludeNativeBlock { name, type_path, associated, methods })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[proc_macro]
|
||||||
|
pub fn include_native(input: TokenStream) -> TokenStream {
|
||||||
|
// Try to parse as simplified syntax: include_native!("name", Type, [m1, m2, ...])
|
||||||
|
if let Ok(simple) = syn::parse::<IncludeNativeSimple>(input.clone()) {
|
||||||
|
return expand_simple_include_native(simple);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to parse as block syntax: include_native!("name", Type, { associated: [...], methods: [...] })
|
||||||
|
if let Ok(block) = syn::parse::<IncludeNativeBlock>(input) {
|
||||||
|
return expand_block_include_native(block);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If both fail, return a compile error
|
||||||
|
syn::Error::new(
|
||||||
|
proc_macro2::Span::call_site(),
|
||||||
|
"Failed to parse include_native! input. Expected:\n \
|
||||||
|
include_native!(\"name\", Type, [method1, method2(arg: Type), ...])\n or\n \
|
||||||
|
include_native!(\"name\", Type, { associated: [func1(arg: Type)], methods: [method1] })",
|
||||||
|
)
|
||||||
|
.to_compile_error()
|
||||||
|
.into()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Generate the argument extraction code for a function spec
|
||||||
|
fn generate_arg_extraction(args: &[(Ident, Type)]) -> Vec<proc_macro2::TokenStream> {
|
||||||
|
args.iter().enumerate().map(|(i, (arg_name, arg_type))| {
|
||||||
|
quote! {
|
||||||
|
let #arg_name = args.get_path(#i)?.native_clone::<#arg_type>()?;
|
||||||
|
}
|
||||||
|
}).collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Generate the argument passing code for a function call
|
||||||
|
fn generate_arg_passing(args: &[(Ident, Type)]) -> Vec<proc_macro2::TokenStream> {
|
||||||
|
args.iter().map(|(arg_name, _)| {
|
||||||
|
quote! { #arg_name }
|
||||||
|
}).collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Last path segment of a Type, for signature strings (e.g. `clap::Command` -> `Command`).
|
||||||
|
fn type_to_sig_name(ty: &Type) -> String {
|
||||||
|
match ty {
|
||||||
|
Type::Path(p) => p
|
||||||
|
.path
|
||||||
|
.segments
|
||||||
|
.last()
|
||||||
|
.map(|s| s.ident.to_string())
|
||||||
|
.unwrap_or_else(|| quote!(#ty).to_string()),
|
||||||
|
_ => quote!(#ty).to_string().replace(' ', ""),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Build a signature string like `"String, Command"` from FuncSpec args.
|
||||||
|
fn synthesize_signature(args: &[(Ident, Type)]) -> String {
|
||||||
|
args.iter()
|
||||||
|
.map(|(_, ty)| type_to_sig_name(ty))
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join(", ")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn expand_simple_include_native(simple: IncludeNativeSimple) -> TokenStream {
|
||||||
|
let name = simple.name;
|
||||||
|
let type_path = simple.type_path;
|
||||||
|
let methods = simple.methods;
|
||||||
|
|
||||||
|
let type_name_str = quote!(#type_path).to_string().replace(' ', "");
|
||||||
|
let method_names: Vec<String> = methods.iter().map(|m| m.name.to_string()).collect();
|
||||||
|
let method_sigs: Vec<String> = methods.iter().map(|m| synthesize_signature(&m.args)).collect();
|
||||||
|
|
||||||
|
let method_calls = methods.iter().map(|method| {
|
||||||
|
let method_name = method.name.to_string();
|
||||||
|
let method_ident = &method.name;
|
||||||
|
let signature = synthesize_signature(&method.args);
|
||||||
|
let arg_extractions = generate_arg_extraction(&method.args);
|
||||||
|
let arg_passings = generate_arg_passing(&method.args);
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
.method(#method_name, #signature, |args| {
|
||||||
|
#(#arg_extractions)*
|
||||||
|
let this = mize::prelude::get_global_mize()?
|
||||||
|
.get_config(#name)?
|
||||||
|
.native_mut::<#type_path>()?;
|
||||||
|
let result = this.#method_ident(#(#arg_passings),*);
|
||||||
|
Ok(mize::item::ItemData::from_native(result)?)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let expanded = quote! {
|
||||||
|
{
|
||||||
|
if let Ok(mize) = mize::prelude::get_global_mize() {
|
||||||
|
if mize.is_generate_mode().unwrap_or(false) {
|
||||||
|
let _ = mize.record_native(
|
||||||
|
#name,
|
||||||
|
#type_name_str,
|
||||||
|
Vec::new(),
|
||||||
|
vec![#(#method_names.to_string()),*],
|
||||||
|
Vec::new(),
|
||||||
|
vec![#(#method_sigs.to_string()),*],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mize::config::opt(#name)
|
||||||
|
#(#method_calls)*
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
TokenStream::from(expanded)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn expand_block_include_native(block: IncludeNativeBlock) -> TokenStream {
|
||||||
|
let name = block.name;
|
||||||
|
let type_path = block.type_path;
|
||||||
|
let associated = block.associated;
|
||||||
|
let methods = block.methods;
|
||||||
|
|
||||||
|
let type_name_str = quote!(#type_path).to_string().replace(' ', "");
|
||||||
|
let associated_names: Vec<String> = associated.iter().map(|m| m.name.to_string()).collect();
|
||||||
|
let method_names: Vec<String> = methods.iter().map(|m| m.name.to_string()).collect();
|
||||||
|
let associated_sigs: Vec<String> = associated.iter().map(|m| synthesize_signature(&m.args)).collect();
|
||||||
|
let method_sigs: Vec<String> = methods.iter().map(|m| synthesize_signature(&m.args)).collect();
|
||||||
|
|
||||||
|
let method_calls = methods.iter().map(|method| {
|
||||||
|
let method_name = method.name.to_string();
|
||||||
|
let method_ident = &method.name;
|
||||||
|
let signature = synthesize_signature(&method.args);
|
||||||
|
let arg_extractions = generate_arg_extraction(&method.args);
|
||||||
|
let arg_passings = generate_arg_passing(&method.args);
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
.method(#method_name, #signature, |args| {
|
||||||
|
#(#arg_extractions)*
|
||||||
|
let this = mize::prelude::get_global_mize()?
|
||||||
|
.get_config(#name)?
|
||||||
|
.native_mut::<#type_path>()?;
|
||||||
|
let result = this.#method_ident(#(#arg_passings),*);
|
||||||
|
Ok(mize::item::ItemData::from_native(result)?)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let associated_calls = associated.iter().map(|func| {
|
||||||
|
let func_name = func.name.to_string();
|
||||||
|
let func_ident = &func.name;
|
||||||
|
let signature = synthesize_signature(&func.args);
|
||||||
|
let arg_extractions = generate_arg_extraction(&func.args);
|
||||||
|
let arg_passings = generate_arg_passing(&func.args);
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
.method(#func_name, #signature, |args| {
|
||||||
|
#(#arg_extractions)*
|
||||||
|
let result = <#type_path>::#func_ident(#(#arg_passings),*);
|
||||||
|
Ok(mize::item::ItemData::from_native(result)?)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let expanded = quote! {
|
||||||
|
{
|
||||||
|
if let Ok(mize) = mize::prelude::get_global_mize() {
|
||||||
|
if mize.is_generate_mode().unwrap_or(false) {
|
||||||
|
let _ = mize.record_native(
|
||||||
|
#name,
|
||||||
|
#type_name_str,
|
||||||
|
vec![#(#associated_names.to_string()),*],
|
||||||
|
vec![#(#method_names.to_string()),*],
|
||||||
|
vec![#(#associated_sigs.to_string()),*],
|
||||||
|
vec![#(#method_sigs.to_string()),*],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mize::config::opt(#name)
|
||||||
|
#(#associated_calls)*
|
||||||
|
#(#method_calls)*
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
TokenStream::from(expanded)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//////////////////////////////////// data! macro ///////////////////////////////////////
|
||||||
|
// A procedural macro to define data in Nix-style and expand to a `ciborium::Value` (CBOR).
|
||||||
|
//
|
||||||
|
// Add this to your `Cargo.toml`:
|
||||||
|
// ```toml
|
||||||
|
// [lib]
|
||||||
|
// proc-macro = true
|
||||||
|
//
|
||||||
|
// [dependencies]
|
||||||
|
// syn = { version = "2.0", features = ["full", "extra-traits"] }
|
||||||
|
// quote = "1.0"
|
||||||
|
// proc-macro2 = "1.0"
|
||||||
|
// ciborium = "0.2"
|
||||||
|
// ```
|
||||||
|
|
||||||
|
use syn::Lit;
|
||||||
|
|
||||||
|
// --- AST ---
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
enum NixExpr {
|
||||||
|
Map(Vec<(String, NixExpr)>),
|
||||||
|
List(Vec<NixExpr>),
|
||||||
|
Literal(Literal),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
enum Literal {
|
||||||
|
String(String),
|
||||||
|
Int(i128),
|
||||||
|
Float(f64),
|
||||||
|
Bool(bool),
|
||||||
|
#[allow(unused)]
|
||||||
|
Null,
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Code Generation ---
|
||||||
|
|
||||||
|
impl NixExpr {
|
||||||
|
fn to_cbor_tokens(&self) -> proc_macro2::TokenStream {
|
||||||
|
match self {
|
||||||
|
NixExpr::Map(entries) => {
|
||||||
|
let entry_tokens: Vec<_> = entries
|
||||||
|
.iter()
|
||||||
|
.map(|(k, v)| {
|
||||||
|
let key = syn::LitStr::new(k, proc_macro2::Span::call_site());
|
||||||
|
let value_tokens = v.to_cbor_tokens();
|
||||||
|
quote! {
|
||||||
|
(mize::ciborium::Value::Text(#key.to_string()), #value_tokens)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
quote! {
|
||||||
|
mize::ciborium::Value::Map({
|
||||||
|
let mut m = Vec::<(mize::ciborium::Value, mize::ciborium::Value)>::new();
|
||||||
|
#(m.push(#entry_tokens);)*
|
||||||
|
m
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
NixExpr::List(items) => {
|
||||||
|
let item_tokens: Vec<_> = items.iter().map(|item| item.to_cbor_tokens()).collect();
|
||||||
|
quote! {
|
||||||
|
mize::ciborium::Value::Array({
|
||||||
|
let mut v = Vec::<mize::ciborium::Value>::new();
|
||||||
|
#(v.push(#item_tokens);)*
|
||||||
|
v
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
NixExpr::Literal(lit) => lit.to_cbor_tokens(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Literal {
|
||||||
|
fn to_cbor_tokens(&self) -> proc_macro2::TokenStream {
|
||||||
|
match self {
|
||||||
|
Literal::String(s) => {
|
||||||
|
let s = syn::LitStr::new(s, proc_macro2::Span::call_site());
|
||||||
|
quote! { mize::ciborium::Value::Text(#s.to_string()) }
|
||||||
|
}
|
||||||
|
Literal::Int(i) => quote! { mize::ciborium::Value::Integer(#i) },
|
||||||
|
Literal::Float(f) => quote! { mize::ciborium::Value::Float(#f) },
|
||||||
|
Literal::Bool(b) => quote! { mize::ciborium::Value::Bool(#b) },
|
||||||
|
Literal::Null => quote! { mize::ciborium::Value::Null },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Parsing ---
|
||||||
|
|
||||||
|
struct NixKeyValue {
|
||||||
|
key: Ident,
|
||||||
|
_eq: Token![=],
|
||||||
|
value: NixExpr,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Parse for NixKeyValue {
|
||||||
|
fn parse(input: ParseStream) -> syn::Result<Self> {
|
||||||
|
Ok(NixKeyValue {
|
||||||
|
key: input.parse()?,
|
||||||
|
_eq: input.parse()?,
|
||||||
|
value: input.parse()?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Parse for NixExpr {
|
||||||
|
fn parse(input: ParseStream) -> syn::Result<Self> {
|
||||||
|
// Parse a map: { a = 1; b = 2; }
|
||||||
|
if input.peek(token::Brace) {
|
||||||
|
let content;
|
||||||
|
syn::braced!(content in input);
|
||||||
|
let pairs = Punctuated::<NixKeyValue, Token![;]>::parse_terminated(&content)?;
|
||||||
|
let entries = pairs
|
||||||
|
.into_iter()
|
||||||
|
.map(|kv| (kv.key.to_string(), kv.value))
|
||||||
|
.collect();
|
||||||
|
return Ok(NixExpr::Map(entries));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse a list: [ 1, 2, 3 ]
|
||||||
|
if input.peek(token::Bracket) {
|
||||||
|
let content;
|
||||||
|
syn::bracketed!(content in input);
|
||||||
|
let items = Punctuated::<NixExpr, Token![,]>::parse_terminated(&content)?;
|
||||||
|
return Ok(NixExpr::List(items.into_iter().collect()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse literals
|
||||||
|
if input.peek(Lit) {
|
||||||
|
let lit = input.parse::<Lit>()?;
|
||||||
|
return match lit {
|
||||||
|
Lit::Str(s) => Ok(NixExpr::Literal(Literal::String(s.value()))),
|
||||||
|
Lit::Int(i) => Ok(NixExpr::Literal(Literal::Int(i.base10_parse::<i128>()?))),
|
||||||
|
Lit::Float(f) => Ok(NixExpr::Literal(Literal::Float(f.base10_parse::<f64>()?))),
|
||||||
|
Lit::Bool(b) => Ok(NixExpr::Literal(Literal::Bool(b.value))),
|
||||||
|
_ => Err(input.error("Unsupported literal")),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
Err(input.error("Expected a Nix expression (map, list, or literal)"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Top-level AttrSet Parsing ---
|
||||||
|
|
||||||
|
struct NixAttrSet(Vec<NixKeyValue>);
|
||||||
|
|
||||||
|
impl syn::parse::Parse for NixAttrSet {
|
||||||
|
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
|
||||||
|
let pairs = Punctuated::<NixKeyValue, Token![;]>::parse_terminated(input)?;
|
||||||
|
Ok(NixAttrSet(pairs.into_iter().collect()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Macro Entry ---
|
||||||
|
|
||||||
|
#[proc_macro]
|
||||||
|
pub fn data(input: TokenStream) -> TokenStream {
|
||||||
|
// Try to parse top-level attrset (without outer braces)
|
||||||
|
let expr = syn::parse::<NixAttrSet>(input.clone())
|
||||||
|
.map(|attrset| {
|
||||||
|
NixExpr::Map(
|
||||||
|
attrset.0
|
||||||
|
.into_iter()
|
||||||
|
.map(|kv| (kv.key.to_string(), kv.value))
|
||||||
|
.collect(),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.or_else(|_| {
|
||||||
|
// fallback: parse legacy behavior (braced map, list, or literal)
|
||||||
|
syn::parse::<NixExpr>(input)
|
||||||
|
});
|
||||||
|
|
||||||
|
let tokens = match expr {
|
||||||
|
Ok(expr) => expr.to_cbor_tokens(),
|
||||||
|
Err(e) => return e.to_compile_error().into(),
|
||||||
|
};
|
||||||
|
TokenStream::from(tokens)
|
||||||
|
}
|
||||||
56
md
56
md
@@ -1,56 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
set -e
|
|
||||||
|
|
||||||
MIZE_SRC="/home/me/work/mize" # hardcoded for now
|
|
||||||
RSYNC_EXCLUDES="--exclude=.git --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" == "dlu" ]]; then
|
|
||||||
deploy_lush
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ "$1" == "dma" ]]; then
|
|
||||||
deploy_main_website
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ "$1" == "dmo" ]]; then
|
|
||||||
deploy_main_obsidian
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function deploy_lush {
|
|
||||||
echo "c2vi: Deploying to lush..."
|
|
||||||
|
|
||||||
# Copy files to lush
|
|
||||||
rsync -r -u -v --delete --mkpath $RSYNC_EXCLUDES $MIZE_SRC/* lush:here/mize
|
|
||||||
|
|
||||||
# compile
|
|
||||||
ssh lush nix develop /home/me/here/mize --impure -v -L -c sh -c "cargo build --release --manifest-path /home/me/here/mize/packages/ppc/Cargo.toml"
|
|
||||||
|
|
||||||
# restart service
|
|
||||||
ssh lush "sudo systemctl restart ppc"
|
|
||||||
}
|
|
||||||
|
|
||||||
function deploy_main_website {
|
|
||||||
echo "c2vi: Deploying website to fes"
|
|
||||||
|
|
||||||
# compile
|
|
||||||
cargo build --release --no-default-features --features server --manifest-path $MIZE_SRC/packages/ppc/Cargo.toml
|
|
||||||
|
|
||||||
# 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 "$@"
|
|
||||||
@@ -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)
|
|
||||||
}
|
|
||||||
@@ -1,32 +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, features = ["string"] }
|
|
||||||
flume = "0.12.0"
|
|
||||||
futures = "0.3.32"
|
|
||||||
mize = { path = "../mize/", default-features = false }
|
|
||||||
tokio = "1.49.0"
|
|
||||||
# https://github.com/rscarson/rustyscript/pull/415
|
|
||||||
rustyscript = { git = 'https://github.com/rscarson/rustyscript', rev = 'e39529eac2c5d37cfcbb7d7d63da7264d7a5afdb', optional = true }
|
|
||||||
#deno_core = { version = "0.380.1", optional = true }
|
|
||||||
chrono = "0.4.44"
|
|
||||||
reqwest = { version = "0.13.2", features = ["blocking", "json"] }
|
|
||||||
serde = "1.0.228"
|
|
||||||
serde_json = "1.0.149"
|
|
||||||
rusqlite = { version = "0.31", features = ["bundled"], optional = true }
|
|
||||||
dioxus = { version = "0.7.1", optional = true }
|
|
||||||
|
|
||||||
[features]
|
|
||||||
default = ["target-os", "desktop", "server"]
|
|
||||||
target-os = [ "clap", "mize/target-os", "rustyscript", "rusqlite", "dioxus" ]
|
|
||||||
desktop = ["dioxus/desktop"]
|
|
||||||
server = []
|
|
||||||
target-js = [ "mize/target-js" ]
|
|
||||||
clap = ["dep:clap"]
|
|
||||||
|
|
||||||
[lib]
|
|
||||||
path = "lib.rs"
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
use dioxus::prelude::*;
|
|
||||||
|
|
||||||
pub fn main() -> Element {
|
|
||||||
rsx! {
|
|
||||||
div {
|
|
||||||
h1 { "my dashbaord" }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,431 +0,0 @@
|
|||||||
import { createClient } from "@libsql/client";
|
|
||||||
import { drizzle } from "drizzle-orm/libsql";
|
|
||||||
import { sqliteTable, text, integer } from "drizzle-orm/sqlite-core";
|
|
||||||
import { eq } from "drizzle-orm";
|
|
||||||
import fs from "node:fs";
|
|
||||||
import path from "node:path";
|
|
||||||
import os from "node:os";
|
|
||||||
import { spawnSync } from "node:child_process";
|
|
||||||
|
|
||||||
export async function init(ppc: PPC) {
|
|
||||||
ppc.c2vi = new C2vi(ppc);
|
|
||||||
|
|
||||||
ppc.cli
|
|
||||||
.command("bed", "command to run when c2vi goes to bed")
|
|
||||||
.action(async () => {
|
|
||||||
await save_habitica_dailies(ppc);
|
|
||||||
await save_habitica_logs(ppc);
|
|
||||||
});
|
|
||||||
|
|
||||||
ppc.cli
|
|
||||||
.command("clearTodos", "command to run when c2vi goes to bed")
|
|
||||||
.alias("clt")
|
|
||||||
.action(async () => {
|
|
||||||
const todos = await ppc.habitica.get_tasks();
|
|
||||||
for (const todo of todos) {
|
|
||||||
await ppc.habitica.delete_task(todo.id);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
ppc.cli
|
|
||||||
.command("listTodos", "command to run when c2vi goes to bed")
|
|
||||||
.alias("dut")
|
|
||||||
.action(async () => {
|
|
||||||
const todos = await ppc.habitica.get_tasks();
|
|
||||||
for (const todo of todos) {
|
|
||||||
console.log(todo.text);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
ppc.cli
|
|
||||||
.command("buyHealthPotion", "command to run when c2vi goes to bed")
|
|
||||||
.alias("buyh")
|
|
||||||
.action(async () => {
|
|
||||||
await ppc.habitica.api_request("POST", "/user/buy-health-potion");
|
|
||||||
});
|
|
||||||
|
|
||||||
ppc.cli
|
|
||||||
.command("addActionItems", "command to run when c2vi goes to bed")
|
|
||||||
.alias("aal")
|
|
||||||
.action(async () => {
|
|
||||||
await add_action_items(ppc);
|
|
||||||
});
|
|
||||||
|
|
||||||
ppc.cli
|
|
||||||
.command("test", "a command to run some test code")
|
|
||||||
.alias("t")
|
|
||||||
.action(async () => {});
|
|
||||||
|
|
||||||
ppc.cli
|
|
||||||
.command("skip [num]", "command to skip a habitica todo")
|
|
||||||
.alias("sk")
|
|
||||||
.action((num = 1) => {
|
|
||||||
skip_habitica_todo(ppc, parseInt(num));
|
|
||||||
});
|
|
||||||
|
|
||||||
ppc.cli.command("dumpLog", "dump the complete habitica log").action(() => {
|
|
||||||
dump_habitica_logs(ppc);
|
|
||||||
});
|
|
||||||
|
|
||||||
ppc.cli
|
|
||||||
.command("listDailies", "list all dailies")
|
|
||||||
.action(async (folder, options) => {
|
|
||||||
const allDailies = await ppc.c2vi.db.select().from(dailiesTable).all();
|
|
||||||
for (const task of allDailies) {
|
|
||||||
console.log(task.id + ": " + task.text);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
ppc.cli
|
|
||||||
.command("printTask <id>", "print a specific todo")
|
|
||||||
.action(async (id) => {
|
|
||||||
const data = await ppc.habitica.api_request("GET", `/tasks/${id}`, {});
|
|
||||||
console.log(data.history);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
class C2vi {
|
|
||||||
[key: string]: any;
|
|
||||||
|
|
||||||
constructor(ppc: PPC) {
|
|
||||||
this.ppc = ppc;
|
|
||||||
this.db = drizzle(
|
|
||||||
createClient({
|
|
||||||
url: "file://" + ppc.config.local_storage_path + "/data.db",
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function formatDate(d) {
|
|
||||||
const pad = (n) => n.toString().padStart(2, "0");
|
|
||||||
|
|
||||||
// Custom YYYY-MM-DD format
|
|
||||||
const formatted = `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}`;
|
|
||||||
return formatted;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ============= SCHEMA DEFINITION =============
|
|
||||||
const dailiesTable = sqliteTable("habitica_dailies", {
|
|
||||||
id: text("id").primaryKey(),
|
|
||||||
text: text("text").notNull(),
|
|
||||||
notes: text("notes"),
|
|
||||||
priority: text("priority"), // 0.1, 1, 1.5, 2
|
|
||||||
frequency: text("frequency"), // 'daily', 'weekly', etc.
|
|
||||||
});
|
|
||||||
const habiticaLogTable = sqliteTable("habitica_log", {
|
|
||||||
date: text("date").primaryKey(),
|
|
||||||
data: text("data").notNull(),
|
|
||||||
});
|
|
||||||
|
|
||||||
async function save_habitica_dailies(ppc) {
|
|
||||||
console.log("######### cli bed cmd #########");
|
|
||||||
await ppc.c2vi.db.run(
|
|
||||||
"CREATE TABLE IF NOT EXISTS habitica_dailies (id TEXT PRIMARY KEY, text TEXT NOT NULL, notes TEXT, priority TEXT, frequency TEXT)",
|
|
||||||
);
|
|
||||||
await ppc.c2vi.db.run(
|
|
||||||
"CREATE TABLE IF NOT EXISTS habitica_log (date TEXT PRIMARY KEY, data TEXT NOT NULL)",
|
|
||||||
);
|
|
||||||
|
|
||||||
// ============= DATABASE INITIALIZATION =============
|
|
||||||
if (!ppc.config.habitica.user_id || !ppc.config.habitica.api_token) {
|
|
||||||
console.error(
|
|
||||||
"Error: HABITICA_USER_ID and HABITICA_API_TOKEN env vars required",
|
|
||||||
);
|
|
||||||
console.error(
|
|
||||||
"Get these from Habitica Settings > API and configure them in the ppc.config",
|
|
||||||
);
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
const dailies = await ppc.habitica.get_tasks("dailys");
|
|
||||||
|
|
||||||
await writeDailiesToDatabase(dailies, ppc);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function dump_habitica_logs(ppc) {
|
|
||||||
const entries = await ppc.c2vi.db.select().from(habiticaLogTable).all();
|
|
||||||
for (const entry of entries) {
|
|
||||||
console.log(entry.date + ": ", JSON.parse(entry.data));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function skip_habitica_todo(ppc, num) {
|
|
||||||
await ppc.c2vi.db.run(
|
|
||||||
"CREATE TABLE IF NOT EXISTS habitica_dailies (id TEXT PRIMARY KEY, text TEXT NOT NULL, notes TEXT, priority TEXT, frequency TEXT)",
|
|
||||||
);
|
|
||||||
await ppc.c2vi.db.run(
|
|
||||||
"CREATE TABLE IF NOT EXISTS habitica_log (date TEXT PRIMARY KEY, data TEXT NOT NULL)",
|
|
||||||
);
|
|
||||||
|
|
||||||
const todos = await ppc.habitica.get_tasks("todos");
|
|
||||||
|
|
||||||
const date = new Date();
|
|
||||||
const save_for_yesterday = date.getHours() < 12;
|
|
||||||
if (save_for_yesterday) {
|
|
||||||
console.log(
|
|
||||||
"Saving logs for yesterday!!!! because it is " +
|
|
||||||
date.getHours() +
|
|
||||||
"hours of the day",
|
|
||||||
);
|
|
||||||
date.setDate(date.getDate() - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
const result = await ppc.c2vi.db
|
|
||||||
.select()
|
|
||||||
.from(habiticaLogTable)
|
|
||||||
.where(eq(habiticaLogTable.date, formatDate(date)));
|
|
||||||
|
|
||||||
const data = result[0]
|
|
||||||
? JSON.parse(result[0].data)
|
|
||||||
: {
|
|
||||||
dailies_done: [],
|
|
||||||
dailies_skipped: [],
|
|
||||||
todos_done: [],
|
|
||||||
todos_skipped: [],
|
|
||||||
};
|
|
||||||
|
|
||||||
for (let i = 0; i < num; i++) {
|
|
||||||
const todo = todos[i];
|
|
||||||
if (!todo) continue;
|
|
||||||
await ppc.habitica.delete_task(todo.id);
|
|
||||||
await incrementHabit(ppc);
|
|
||||||
|
|
||||||
data.todos_skipped.push({
|
|
||||||
id: todo.id,
|
|
||||||
text: todo.text,
|
|
||||||
notes: todo.notes || "",
|
|
||||||
checklist: todo.checklist || [],
|
|
||||||
tags: todo.tags || [],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// saving to db
|
|
||||||
await ppc.c2vi.db
|
|
||||||
.insert(habiticaLogTable)
|
|
||||||
.values({ date: formatDate(date), data: JSON.stringify(data) })
|
|
||||||
.onConflictDoUpdate({
|
|
||||||
target: habiticaLogTable.date,
|
|
||||||
set: {
|
|
||||||
data: JSON.stringify(data),
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async function save_habitica_logs(ppc) {
|
|
||||||
const date = new Date();
|
|
||||||
const save_for_yesterday = date.getHours() < 12;
|
|
||||||
if (save_for_yesterday) {
|
|
||||||
console.log(
|
|
||||||
"Saving logs for yesterday!!!! because it is " +
|
|
||||||
date.getHours() +
|
|
||||||
"hours of the day",
|
|
||||||
);
|
|
||||||
date.setDate(date.getDate() - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
const result = await ppc.c2vi.db
|
|
||||||
.select()
|
|
||||||
.from(habiticaLogTable)
|
|
||||||
.where(eq(habiticaLogTable.date, formatDate(date)));
|
|
||||||
|
|
||||||
const data = result[0]
|
|
||||||
? JSON.parse(result[0].data)
|
|
||||||
: {
|
|
||||||
dailies_done: [],
|
|
||||||
dailies_skipped: [],
|
|
||||||
todos_done: [],
|
|
||||||
todos_skipped: [],
|
|
||||||
};
|
|
||||||
|
|
||||||
// dailies
|
|
||||||
const dailies = await ppc.habitica.get_tasks("dailys");
|
|
||||||
for (const daily of dailies) {
|
|
||||||
let completed = daily.completed;
|
|
||||||
let isDue = daily.isDue;
|
|
||||||
if (save_for_yesterday) {
|
|
||||||
const yesterday = daily.history.reduce((prev, current) => {
|
|
||||||
return prev.date > current.date ? prev : current;
|
|
||||||
});
|
|
||||||
completed = yesterday.completed;
|
|
||||||
isDue = yesterday.isDue;
|
|
||||||
}
|
|
||||||
if (!isDue) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (completed) {
|
|
||||||
data.dailies_done.push(daily.id);
|
|
||||||
console.log("daily-done: " + daily.text);
|
|
||||||
} else {
|
|
||||||
data.dailies_skipped.push(daily.id);
|
|
||||||
console.log("daily-skipped: " + daily.text);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// todos done
|
|
||||||
const todos = await ppc.habitica.get_tasks("completedTodos");
|
|
||||||
for (const todo of todos) {
|
|
||||||
const dateCompleted = new Date(todo.dateCompleted);
|
|
||||||
|
|
||||||
// check if this todo is already saved in yesterday's log entry
|
|
||||||
if (save_for_yesterday) {
|
|
||||||
// get yesterday log entry
|
|
||||||
const result = await ppc.c2vi.db
|
|
||||||
.select()
|
|
||||||
.from(habiticaLogTable)
|
|
||||||
.where(eq(habiticaLogTable.date, formatDate(date)));
|
|
||||||
|
|
||||||
const data = result[0]
|
|
||||||
? JSON.parse(result[0].data)
|
|
||||||
: {
|
|
||||||
dailies_done: [],
|
|
||||||
dailies_skipped: [],
|
|
||||||
todos_done: [],
|
|
||||||
todos_skipped: [],
|
|
||||||
};
|
|
||||||
|
|
||||||
// check if this todo is already saved in yesterday's log entry
|
|
||||||
const todoIndex = data.todos_done.findIndex((t) => t.id === todo.id);
|
|
||||||
if (todoIndex !== -1) {
|
|
||||||
continue; // skipp saving this todo
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dateCompleted.toDateString() == date.toDateString()) {
|
|
||||||
console.log("todo-done:", todo.text);
|
|
||||||
data.todos_done.push({
|
|
||||||
id: todo.id,
|
|
||||||
text: todo.text,
|
|
||||||
notes: todo.notes || "",
|
|
||||||
checklist: todo.checklist || [],
|
|
||||||
tags: todo.tags || [],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// we save_for_yesterday but the todo was completed today... also save that for this day
|
|
||||||
if (
|
|
||||||
save_for_yesterday &&
|
|
||||||
dateCompleted.toDateString() == new Date().toDateString()
|
|
||||||
) {
|
|
||||||
console.log("todo-done:", todo.text);
|
|
||||||
data.todos_done.push({
|
|
||||||
id: todo.id,
|
|
||||||
text: todo.text,
|
|
||||||
notes: todo.notes || "",
|
|
||||||
checklist: todo.checklist || [],
|
|
||||||
tags: todo.tags || [],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// saving to db
|
|
||||||
await ppc.c2vi.db
|
|
||||||
.insert(habiticaLogTable)
|
|
||||||
.values({ date: formatDate(date), data: JSON.stringify(data) })
|
|
||||||
.onConflictDoUpdate({
|
|
||||||
target: habiticaLogTable.date,
|
|
||||||
set: {
|
|
||||||
data: JSON.stringify(data),
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async function writeDailiesToDatabase(dailies, ppc) {
|
|
||||||
console.log(`Syncing ${dailies.length} dailies to database...`);
|
|
||||||
|
|
||||||
const records = dailies.map((daily) => ({
|
|
||||||
id: daily.id,
|
|
||||||
text: daily.text,
|
|
||||||
notes: daily.notes || "",
|
|
||||||
priority: daily.priority?.toString() || null,
|
|
||||||
frequency: daily.frequency || "daily",
|
|
||||||
}));
|
|
||||||
|
|
||||||
async function upsertMany(records) {
|
|
||||||
for (const record of records) {
|
|
||||||
await ppc.c2vi.db
|
|
||||||
.insert(dailiesTable)
|
|
||||||
.values(record)
|
|
||||||
.onConflictDoUpdate({
|
|
||||||
target: dailiesTable.id,
|
|
||||||
set: {
|
|
||||||
text: record.text,
|
|
||||||
notes: record.notes,
|
|
||||||
priority: record.priority,
|
|
||||||
frequency: record.frequency,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
await upsertMany(records);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function incrementHabit(ppc) {
|
|
||||||
const habits = await ppc.habitica.get_tasks("habits");
|
|
||||||
let id = "";
|
|
||||||
for (const habit of habits) {
|
|
||||||
if ((habit.text = "skipped a planned task")) {
|
|
||||||
id = habit.id;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
await ppc.habitica.api_request("POST", `tasks/${id}/score/down`);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function add_action_items(ppc: PPC) {
|
|
||||||
let task_ids = [];
|
|
||||||
|
|
||||||
const tmpFile = path.join(os.tmpdir(), `habitica-tasks-${Date.now()}.md`);
|
|
||||||
const initialContent = `
|
|
||||||
gu 10min TIMER
|
|
||||||
uw 15min FROG
|
|
||||||
b coffee/tee/kakau
|
|
||||||
uw FROG
|
|
||||||
b breakfast, brush teeth, lüften
|
|
||||||
uw FROG
|
|
||||||
|
|
||||||
lunch
|
|
||||||
|
|
||||||
uw 15min TIMER of next days FROG
|
|
||||||
fw
|
|
||||||
dt 30min TIMER
|
|
||||||
|
|
||||||
fun 20:00 (smth that can be stopped easily)
|
|
||||||
se 21:40
|
|
||||||
read
|
|
||||||
bed 22:10
|
|
||||||
`;
|
|
||||||
|
|
||||||
fs.writeFileSync(tmpFile, initialContent.trim());
|
|
||||||
|
|
||||||
spawnSync("nvim", [tmpFile], { stdio: "inherit" });
|
|
||||||
|
|
||||||
// read file
|
|
||||||
const listStr = fs.readFileSync(tmpFile, "utf8");
|
|
||||||
|
|
||||||
// delete file
|
|
||||||
fs.unlinkSync(tmpFile);
|
|
||||||
|
|
||||||
// Simple parser: filter out empty lines
|
|
||||||
const tasks = listStr
|
|
||||||
.split("\n")
|
|
||||||
.map((t) => t.trim())
|
|
||||||
.filter((t) => t.length > 0);
|
|
||||||
|
|
||||||
// 5. Add tasks to Habitica
|
|
||||||
for (const todoText of tasks) {
|
|
||||||
const data = await ppc.habitica.api_request(
|
|
||||||
"POST",
|
|
||||||
"tasks/user",
|
|
||||||
{},
|
|
||||||
{ text: todoText, type: "todo" },
|
|
||||||
);
|
|
||||||
task_ids.push(data.id);
|
|
||||||
console.log(`Added to-do: '${todoText}' with id '${data.id}'`);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 6. Move tasks to bottom
|
|
||||||
for (const taskId of task_ids) {
|
|
||||||
await ppc.habitica.api_request("POST", `tasks/${taskId}/move/to/-1`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,607 +0,0 @@
|
|||||||
use std::sync::Mutex;
|
|
||||||
|
|
||||||
use chrono::{Timelike, Utc};
|
|
||||||
use clap::{Arg, Command};
|
|
||||||
use rusqlite::{params, Connection};
|
|
||||||
use serde_json::{json, Value};
|
|
||||||
|
|
||||||
use mize::{mize_err, mize_part, Mize, MizeError, MizePart, MizeResult};
|
|
||||||
|
|
||||||
use crate::cli::CliPart;
|
|
||||||
use crate::habitica::Habitica;
|
|
||||||
|
|
||||||
mod dashboard;
|
|
||||||
|
|
||||||
#[mize_part]
|
|
||||||
#[derive(Default)]
|
|
||||||
pub struct C2vi {
|
|
||||||
mize: Mize,
|
|
||||||
db: Mutex<Option<Connection>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl MizePart for C2vi {
|
|
||||||
fn opts(&self, mize: &mut Mize) {
|
|
||||||
mize.new_opt("c2vi.local_storage_path");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl C2vi {
|
|
||||||
fn with_db<F, R>(&self, f: F) -> MizeResult<R>
|
|
||||||
where
|
|
||||||
F: FnOnce(&Connection) -> MizeResult<R>,
|
|
||||||
{
|
|
||||||
let guard = self
|
|
||||||
.db
|
|
||||||
.lock()
|
|
||||||
.map_err(|e| mize_err!("DB lock poisoned: {}", e))?;
|
|
||||||
let conn = guard
|
|
||||||
.as_ref()
|
|
||||||
.ok_or_else(|| mize_err!("DB not initialized"))?;
|
|
||||||
f(conn)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn c2vi(mize: &mut Mize) -> MizeResult<()> {
|
|
||||||
#[cfg(feature = "client")]
|
|
||||||
c2vi_client(mize)?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn c2vi_client(mize: &mut Mize) -> MizeResult<()> {
|
|
||||||
//let storage_path = mize.get_config("c2vi.local_storage_path")?.as_
|
|
||||||
let storage_path = "/home/me/work/app-data/ppc";
|
|
||||||
println!("storage_path: {}", storage_path);
|
|
||||||
let db_path = std::path::PathBuf::from(&storage_path).join("data.db");
|
|
||||||
let conn = Connection::open(&db_path)
|
|
||||||
.map_err(|e| mize_err!("Failed to open C2vi database at {:?}: {}", db_path, e))?;
|
|
||||||
|
|
||||||
setup_tables(&conn)?;
|
|
||||||
|
|
||||||
let c2vi_part = C2vi {
|
|
||||||
mize: mize.clone(),
|
|
||||||
db: Mutex::new(Some(conn)),
|
|
||||||
};
|
|
||||||
mize.register_part(Box::new(c2vi_part))?;
|
|
||||||
|
|
||||||
// Register CLI subcommands
|
|
||||||
let mut cli = mize.get_part_native::<CliPart>("cli")?;
|
|
||||||
register_subcommands(&mut cli);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn setup_tables(conn: &Connection) -> MizeResult<()> {
|
|
||||||
conn.execute(
|
|
||||||
"CREATE TABLE IF NOT EXISTS habitica_dailies (
|
|
||||||
id TEXT PRIMARY KEY,
|
|
||||||
text TEXT NOT NULL,
|
|
||||||
notes TEXT,
|
|
||||||
priority TEXT,
|
|
||||||
frequency TEXT
|
|
||||||
)",
|
|
||||||
[],
|
|
||||||
)
|
|
||||||
.map_err(|e| mize_err!("DB error creating habitica_dailies: {}", e))?;
|
|
||||||
conn.execute(
|
|
||||||
"CREATE TABLE IF NOT EXISTS habitica_log (
|
|
||||||
date TEXT PRIMARY KEY,
|
|
||||||
data TEXT NOT NULL
|
|
||||||
)",
|
|
||||||
[],
|
|
||||||
)
|
|
||||||
.map_err(|e| mize_err!("DB error creating habitica_log: {}", e))?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn register_subcommands(cli: &mut CliPart) {
|
|
||||||
cli.subcommand(
|
|
||||||
Command::new("c2vi-dash").about("Run the c2vi dashboard gui"),
|
|
||||||
|_matches, mut mize| Ok(dioxus::launch(dashboard::main)),
|
|
||||||
);
|
|
||||||
cli.subcommand(
|
|
||||||
Command::new("bed").about("Command to run when c2vi goes to bed"),
|
|
||||||
|_matches, mut mize| {
|
|
||||||
save_habitica_dailies(&mut mize)?;
|
|
||||||
save_habitica_logs(&mut mize)?;
|
|
||||||
Ok(())
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
cli.subcommand(
|
|
||||||
Command::new("clearTodos")
|
|
||||||
.about("Clear all Habitica todos")
|
|
||||||
.alias("clt"),
|
|
||||||
|_matches, mut mize| {
|
|
||||||
let todos = {
|
|
||||||
let mut hab = mize.get_part_native::<Habitica>("habitica")?;
|
|
||||||
hab.get_tasks("todos")?
|
|
||||||
};
|
|
||||||
let mut hab = mize.get_part_native::<Habitica>("habitica")?;
|
|
||||||
if let Some(arr) = todos.as_array() {
|
|
||||||
for todo in arr {
|
|
||||||
if let Some(id) = todo["id"].as_str() {
|
|
||||||
hab.delete_task(id)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
cli.subcommand(
|
|
||||||
Command::new("listTodos")
|
|
||||||
.about("List all Habitica todos")
|
|
||||||
.alias("dut"),
|
|
||||||
|_matches, mut mize| {
|
|
||||||
let mut hab = mize.get_part_native::<Habitica>("habitica")?;
|
|
||||||
let todos = hab.get_tasks("todos")?;
|
|
||||||
if let Some(arr) = todos.as_array() {
|
|
||||||
for todo in arr {
|
|
||||||
if let Some(text) = todo["text"].as_str() {
|
|
||||||
println!("{}", text);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
cli.subcommand(
|
|
||||||
Command::new("buyHealthPotion")
|
|
||||||
.about("Buy a health potion")
|
|
||||||
.alias("buyh"),
|
|
||||||
|_matches, mut mize| {
|
|
||||||
let mut hab = mize.get_part_native::<Habitica>("habitica")?;
|
|
||||||
hab.api_request(
|
|
||||||
reqwest::Method::POST,
|
|
||||||
"user/buy-health-potion".to_string(),
|
|
||||||
json!({}),
|
|
||||||
)?;
|
|
||||||
Ok(())
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
cli.subcommand(
|
|
||||||
Command::new("addActionItems")
|
|
||||||
.about("Add action items from temp file")
|
|
||||||
.alias("aal"),
|
|
||||||
|_matches, mut mize| add_action_items(&mut mize),
|
|
||||||
);
|
|
||||||
|
|
||||||
cli.subcommand(
|
|
||||||
Command::new("skip")
|
|
||||||
.about("Skip a Habitica todo")
|
|
||||||
.alias("sk")
|
|
||||||
.arg(Arg::new("num").default_value("1")),
|
|
||||||
|matches, mut mize| {
|
|
||||||
let num: usize = matches
|
|
||||||
.get_one::<String>("num")
|
|
||||||
.unwrap()
|
|
||||||
.parse()
|
|
||||||
.unwrap_or(1);
|
|
||||||
skip_habitica_todo(&mut mize, num)
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
cli.subcommand(
|
|
||||||
Command::new("dumpLog").about("Dump the complete Habitica log"),
|
|
||||||
|_matches, mut mize| dump_habitica_logs(&mut mize),
|
|
||||||
);
|
|
||||||
|
|
||||||
cli.subcommand(
|
|
||||||
Command::new("listDailies").about("List all dailies from local DB"),
|
|
||||||
|_matches, mut mize| {
|
|
||||||
let c2vi = mize.get_part_native::<C2vi>("c2vi")?;
|
|
||||||
c2vi.with_db(|conn| {
|
|
||||||
let mut stmt = conn
|
|
||||||
.prepare("SELECT id, text FROM habitica_dailies")
|
|
||||||
.map_err(|e| mize_err!("DB error: {}", e))?;
|
|
||||||
let rows = stmt
|
|
||||||
.query_map([], |row| {
|
|
||||||
Ok((row.get::<_, String>(0)?, row.get::<_, String>(1)?))
|
|
||||||
})
|
|
||||||
.map_err(|e| mize_err!("DB error: {}", e))?;
|
|
||||||
for row in rows {
|
|
||||||
let (id, text) = row.map_err(|e| mize_err!("DB error: {}", e))?;
|
|
||||||
println!("{}: {}", id, text);
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
})
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
cli.subcommand(
|
|
||||||
Command::new("printTask")
|
|
||||||
.about("Print a specific task from Habitica")
|
|
||||||
.arg(Arg::new("id").required(true)),
|
|
||||||
|matches, mut mize| {
|
|
||||||
let id = matches.get_one::<String>("id").unwrap();
|
|
||||||
let mut hab = mize.get_part_native::<Habitica>("habitica")?;
|
|
||||||
let data = hab.api_request(reqwest::Method::GET, format!("tasks/{}", id), json!({}))?;
|
|
||||||
if let Some(history) = data.get("history") {
|
|
||||||
println!(
|
|
||||||
"{}",
|
|
||||||
serde_json::to_string_pretty(history).unwrap_or_default()
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
println!(
|
|
||||||
"{}",
|
|
||||||
serde_json::to_string_pretty(&data).unwrap_or_default()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ============= Business Logic =============
|
|
||||||
|
|
||||||
fn format_date(dt: chrono::DateTime<Utc>) -> String {
|
|
||||||
dt.format("%Y-%m-%d").to_string()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get the target date, adjusted for "save for yesterday" logic (before noon = yesterday)
|
|
||||||
fn get_target_date() -> (String, bool) {
|
|
||||||
let now = Utc::now();
|
|
||||||
let save_for_yesterday = now.hour() < 12;
|
|
||||||
let date = if save_for_yesterday {
|
|
||||||
now - chrono::Duration::days(1)
|
|
||||||
} else {
|
|
||||||
now
|
|
||||||
};
|
|
||||||
if save_for_yesterday {
|
|
||||||
println!(
|
|
||||||
"Saving logs for yesterday because it is {} hours of the day",
|
|
||||||
now.hour()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
(format_date(date), save_for_yesterday)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Load existing log entry from DB, or return a default empty one
|
|
||||||
fn get_or_create_log(conn: &Connection, date: &str) -> MizeResult<Value> {
|
|
||||||
let mut stmt = conn
|
|
||||||
.prepare("SELECT data FROM habitica_log WHERE date = ?1")
|
|
||||||
.map_err(|e| mize_err!("DB error: {}", e))?;
|
|
||||||
let result: Option<String> = stmt.query_row(params![date], |row| row.get(0)).ok();
|
|
||||||
match result {
|
|
||||||
Some(data_str) => {
|
|
||||||
serde_json::from_str(&data_str).map_err(|e| mize_err!("JSON parse error: {}", e))
|
|
||||||
}
|
|
||||||
None => Ok(json!({
|
|
||||||
"dailies_done": [],
|
|
||||||
"dailies_skipped": [],
|
|
||||||
"todos_done": [],
|
|
||||||
"todos_skipped": []
|
|
||||||
})),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Upsert a log entry into the database
|
|
||||||
fn upsert_log(conn: &Connection, date: &str, data: &Value) -> MizeResult<()> {
|
|
||||||
let data_str =
|
|
||||||
serde_json::to_string(data).map_err(|e| mize_err!("JSON serialize error: {}", e))?;
|
|
||||||
conn.execute(
|
|
||||||
"INSERT INTO habitica_log (date, data) VALUES (?1, ?2)
|
|
||||||
ON CONFLICT(date) DO UPDATE SET data=excluded.data",
|
|
||||||
params![date, data_str],
|
|
||||||
)
|
|
||||||
.map_err(|e| mize_err!("DB error upserting log: {}", e))?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn save_habitica_dailies(mize: &mut Mize) -> MizeResult<()> {
|
|
||||||
println!("######### cli bed cmd #########");
|
|
||||||
|
|
||||||
// Fetch dailies from Habitica
|
|
||||||
let dailies = {
|
|
||||||
let mut hab = mize.get_part_native::<Habitica>("habitica")?;
|
|
||||||
hab.get_tasks("dailys")?
|
|
||||||
};
|
|
||||||
|
|
||||||
// Write to database
|
|
||||||
let c2vi = mize.get_part_native::<C2vi>("c2vi")?;
|
|
||||||
c2vi.with_db(|conn| {
|
|
||||||
if let Some(arr) = dailies.as_array() {
|
|
||||||
println!("Syncing {} dailies to database...", arr.len());
|
|
||||||
for daily in arr {
|
|
||||||
let id = daily["id"].as_str().unwrap_or("");
|
|
||||||
let text = daily["text"].as_str().unwrap_or("");
|
|
||||||
let notes = daily["notes"].as_str().unwrap_or("");
|
|
||||||
let priority = daily["priority"].to_string();
|
|
||||||
let frequency = daily["frequency"].as_str().unwrap_or("daily");
|
|
||||||
|
|
||||||
conn.execute(
|
|
||||||
"INSERT INTO habitica_dailies (id, text, notes, priority, frequency)
|
|
||||||
VALUES (?1, ?2, ?3, ?4, ?5)
|
|
||||||
ON CONFLICT(id) DO UPDATE SET
|
|
||||||
text=excluded.text,
|
|
||||||
notes=excluded.notes,
|
|
||||||
priority=excluded.priority,
|
|
||||||
frequency=excluded.frequency",
|
|
||||||
params![id, text, notes, priority, frequency],
|
|
||||||
)
|
|
||||||
.map_err(|e| mize_err!("DB error writing daily: {}", e))?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn save_habitica_logs(mize: &mut Mize) -> MizeResult<()> {
|
|
||||||
let (date_str, save_for_yesterday) = get_target_date();
|
|
||||||
|
|
||||||
// Fetch dailies and completed todos from Habitica
|
|
||||||
let (dailies, completed_todos) = {
|
|
||||||
let mut hab = mize.get_part_native::<Habitica>("habitica")?;
|
|
||||||
let dailies = hab.get_tasks("dailys")?;
|
|
||||||
let completed_todos = hab.get_tasks("completedTodos")?;
|
|
||||||
(dailies, completed_todos)
|
|
||||||
};
|
|
||||||
|
|
||||||
let c2vi = mize.get_part_native::<C2vi>("c2vi")?;
|
|
||||||
c2vi.with_db(|conn| {
|
|
||||||
let mut data = get_or_create_log(conn, &date_str)?;
|
|
||||||
|
|
||||||
// Process dailies
|
|
||||||
if let Some(arr) = dailies.as_array() {
|
|
||||||
for daily in arr {
|
|
||||||
let is_due = if save_for_yesterday {
|
|
||||||
// Look at history for yesterday's status
|
|
||||||
daily["history"]
|
|
||||||
.as_array()
|
|
||||||
.and_then(|h| h.iter().max_by_key(|e| e["date"].as_i64().unwrap_or(0)))
|
|
||||||
.map(|e| e["isDue"].as_bool().unwrap_or(false))
|
|
||||||
.unwrap_or(false)
|
|
||||||
} else {
|
|
||||||
daily["isDue"].as_bool().unwrap_or(false)
|
|
||||||
};
|
|
||||||
|
|
||||||
if !is_due {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
let completed = if save_for_yesterday {
|
|
||||||
daily["history"]
|
|
||||||
.as_array()
|
|
||||||
.and_then(|h| h.iter().max_by_key(|e| e["date"].as_i64().unwrap_or(0)))
|
|
||||||
.map(|e| e["completed"].as_bool().unwrap_or(false))
|
|
||||||
.unwrap_or(false)
|
|
||||||
} else {
|
|
||||||
daily["completed"].as_bool().unwrap_or(false)
|
|
||||||
};
|
|
||||||
|
|
||||||
let daily_id = daily["id"].as_str().unwrap_or("");
|
|
||||||
let daily_text = daily["text"].as_str().unwrap_or("");
|
|
||||||
|
|
||||||
if completed {
|
|
||||||
data["dailies_done"]
|
|
||||||
.as_array_mut()
|
|
||||||
.unwrap()
|
|
||||||
.push(json!(daily_id));
|
|
||||||
println!("daily-done: {}", daily_text);
|
|
||||||
} else {
|
|
||||||
data["dailies_skipped"]
|
|
||||||
.as_array_mut()
|
|
||||||
.unwrap()
|
|
||||||
.push(json!(daily_id));
|
|
||||||
println!("daily-skipped: {}", daily_text);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Process completed todos
|
|
||||||
if let Some(arr) = completed_todos.as_array() {
|
|
||||||
let today_str = format_date(Utc::now());
|
|
||||||
for todo in arr {
|
|
||||||
let date_completed_str = todo["dateCompleted"].as_str().unwrap_or("");
|
|
||||||
|
|
||||||
// Parse the completion date (ISO 8601 format from Habitica)
|
|
||||||
let date_completed = chrono::DateTime::parse_from_rfc3339(date_completed_str)
|
|
||||||
.map(|d| format_date(d.with_timezone(&Utc)))
|
|
||||||
.unwrap_or_default();
|
|
||||||
|
|
||||||
// Check if already in the log for this date
|
|
||||||
if save_for_yesterday {
|
|
||||||
let existing_todos = data["todos_done"].as_array().unwrap();
|
|
||||||
let todo_id = todo["id"].as_str().unwrap_or("");
|
|
||||||
if existing_todos
|
|
||||||
.iter()
|
|
||||||
.any(|t| t["id"].as_str() == Some(todo_id))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let todo_entry = json!({
|
|
||||||
"id": todo["id"],
|
|
||||||
"text": todo["text"],
|
|
||||||
"notes": todo.get("notes").unwrap_or(&json!("")),
|
|
||||||
"checklist": todo.get("checklist").unwrap_or(&json!([])),
|
|
||||||
"tags": todo.get("tags").unwrap_or(&json!([])),
|
|
||||||
});
|
|
||||||
|
|
||||||
// Save if completed on the target date
|
|
||||||
if date_completed == date_str {
|
|
||||||
println!("todo-done: {}", todo["text"].as_str().unwrap_or(""));
|
|
||||||
data["todos_done"]
|
|
||||||
.as_array_mut()
|
|
||||||
.unwrap()
|
|
||||||
.push(todo_entry.clone());
|
|
||||||
}
|
|
||||||
|
|
||||||
// If saving for yesterday but todo was completed today, also record it
|
|
||||||
if save_for_yesterday && date_completed == today_str {
|
|
||||||
println!("todo-done: {}", todo["text"].as_str().unwrap_or(""));
|
|
||||||
data["todos_done"].as_array_mut().unwrap().push(todo_entry);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
upsert_log(conn, &date_str, &data)?;
|
|
||||||
Ok(())
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn skip_habitica_todo(mize: &mut Mize, num: usize) -> MizeResult<()> {
|
|
||||||
let (date_str, _) = get_target_date();
|
|
||||||
|
|
||||||
// Fetch todos from Habitica
|
|
||||||
let todos = {
|
|
||||||
let mut hab = mize.get_part_native::<Habitica>("habitica")?;
|
|
||||||
hab.get_tasks("todos")?
|
|
||||||
};
|
|
||||||
|
|
||||||
let arr = todos
|
|
||||||
.as_array()
|
|
||||||
.ok_or_else(|| mize_err!("Expected array of todos"))?;
|
|
||||||
|
|
||||||
// Delete and log each skipped todo
|
|
||||||
for i in 0..num {
|
|
||||||
let todo = match arr.get(i) {
|
|
||||||
Some(t) => t,
|
|
||||||
None => continue,
|
|
||||||
};
|
|
||||||
|
|
||||||
let todo_id = todo["id"].as_str().unwrap_or("");
|
|
||||||
|
|
||||||
// Delete the todo and score the skip habit
|
|
||||||
{
|
|
||||||
let mut hab = mize.get_part_native::<Habitica>("habitica")?;
|
|
||||||
hab.delete_task(todo_id)?;
|
|
||||||
}
|
|
||||||
increment_habit(mize)?;
|
|
||||||
|
|
||||||
// Record in log
|
|
||||||
let c2vi = mize.get_part_native::<C2vi>("c2vi")?;
|
|
||||||
c2vi.with_db(|conn| {
|
|
||||||
let mut data = get_or_create_log(conn, &date_str)?;
|
|
||||||
data["todos_skipped"].as_array_mut().unwrap().push(json!({
|
|
||||||
"id": todo["id"],
|
|
||||||
"text": todo["text"],
|
|
||||||
"notes": todo.get("notes").unwrap_or(&json!("")),
|
|
||||||
"checklist": todo.get("checklist").unwrap_or(&json!([])),
|
|
||||||
"tags": todo.get("tags").unwrap_or(&json!([])),
|
|
||||||
}));
|
|
||||||
upsert_log(conn, &date_str, &data)?;
|
|
||||||
Ok(())
|
|
||||||
})?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn increment_habit(mize: &mut Mize) -> MizeResult<()> {
|
|
||||||
let mut hab = mize.get_part_native::<Habitica>("habitica")?;
|
|
||||||
let habits = hab.get_tasks("habits")?;
|
|
||||||
if let Some(arr) = habits.as_array() {
|
|
||||||
for habit in arr {
|
|
||||||
if habit["text"].as_str() == Some("skipped a planned task") {
|
|
||||||
let id = habit["id"].as_str().unwrap_or("");
|
|
||||||
hab.api_request(
|
|
||||||
reqwest::Method::POST,
|
|
||||||
format!("tasks/{}/score/down", id),
|
|
||||||
json!({}),
|
|
||||||
)?;
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn add_action_items(mize: &mut Mize) -> MizeResult<()> {
|
|
||||||
let initial_content = "\
|
|
||||||
gu 10min TIMER
|
|
||||||
uw 15min FROG
|
|
||||||
b coffee/tee/kakau
|
|
||||||
uw FROG
|
|
||||||
b breakfast, brush teeth, lüften
|
|
||||||
uw FROG
|
|
||||||
|
|
||||||
lunch
|
|
||||||
|
|
||||||
uw 15min TIMER of next days FROG
|
|
||||||
fw
|
|
||||||
dt 30min TIMER
|
|
||||||
|
|
||||||
fun 20:00 (smth that can be stopped easily)
|
|
||||||
se 21:40
|
|
||||||
read
|
|
||||||
bed 22:10";
|
|
||||||
|
|
||||||
let tmp_file =
|
|
||||||
std::env::temp_dir().join(format!("habitica-tasks-{}.md", Utc::now().timestamp()));
|
|
||||||
std::fs::write(&tmp_file, initial_content)
|
|
||||||
.map_err(|e| mize_err!("Failed to write temp file: {}", e))?;
|
|
||||||
|
|
||||||
// Open in nvim for editing
|
|
||||||
std::process::Command::new("nvim")
|
|
||||||
.arg(&tmp_file)
|
|
||||||
.status()
|
|
||||||
.map_err(|e| mize_err!("Failed to run nvim: {}", e))?;
|
|
||||||
|
|
||||||
// Read back and parse
|
|
||||||
let list_str = std::fs::read_to_string(&tmp_file)
|
|
||||||
.map_err(|e| mize_err!("Failed to read temp file: {}", e))?;
|
|
||||||
let _ = std::fs::remove_file(&tmp_file);
|
|
||||||
|
|
||||||
let tasks: Vec<&str> = list_str
|
|
||||||
.lines()
|
|
||||||
.map(|l| l.trim())
|
|
||||||
.filter(|l| !l.is_empty())
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
// Add tasks to Habitica
|
|
||||||
let mut task_ids = Vec::new();
|
|
||||||
{
|
|
||||||
let mut hab = mize.get_part_native::<Habitica>("habitica")?;
|
|
||||||
for todo_text in &tasks {
|
|
||||||
let data = hab.api_request(
|
|
||||||
reqwest::Method::POST,
|
|
||||||
"tasks/user".to_string(),
|
|
||||||
json!({ "text": todo_text, "type": "todo" }),
|
|
||||||
)?;
|
|
||||||
let id = data["id"].as_str().unwrap_or("").to_string();
|
|
||||||
println!("Added to-do: '{}' with id '{}'", todo_text, id);
|
|
||||||
task_ids.push(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Move all tasks to bottom
|
|
||||||
for task_id in &task_ids {
|
|
||||||
hab.api_request(
|
|
||||||
reqwest::Method::POST,
|
|
||||||
format!("tasks/{}/move/to/-1", task_id),
|
|
||||||
json!({}),
|
|
||||||
)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn dump_habitica_logs(mize: &mut Mize) -> MizeResult<()> {
|
|
||||||
let c2vi = mize.get_part_native::<C2vi>("c2vi")?;
|
|
||||||
c2vi.with_db(|conn| {
|
|
||||||
let mut stmt = conn
|
|
||||||
.prepare("SELECT date, data FROM habitica_log ORDER BY date")
|
|
||||||
.map_err(|e| mize_err!("DB error: {}", e))?;
|
|
||||||
let rows = stmt
|
|
||||||
.query_map([], |row| {
|
|
||||||
Ok((row.get::<_, String>(0)?, row.get::<_, String>(1)?))
|
|
||||||
})
|
|
||||||
.map_err(|e| mize_err!("DB error: {}", e))?;
|
|
||||||
for row in rows {
|
|
||||||
let (date, data_str) = row.map_err(|e| mize_err!("DB error: {}", e))?;
|
|
||||||
let data: Value =
|
|
||||||
serde_json::from_str(&data_str).unwrap_or(json!({"parse_error": data_str}));
|
|
||||||
println!(
|
|
||||||
"{}: {}",
|
|
||||||
date,
|
|
||||||
serde_json::to_string_pretty(&data).unwrap_or_default()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
})
|
|
||||||
}
|
|
||||||
@@ -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);
|
|
||||||
}
|
|
||||||
@@ -1,100 +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,
|
|
||||||
Box<dyn FnOnce(&ArgMatches, Mize) -> MizeResult<()> + Send + Sync + 'static>,
|
|
||||||
>,
|
|
||||||
parsers:
|
|
||||||
Option<Vec<Box<dyn FnOnce(Mize, Vec<String>) -> MizeResult<()> + Send + Sync + 'static>>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn cli(mize: &mut Mize) -> MizeResult<()> {
|
|
||||||
let command = Command::new("marts-cli").allow_external_subcommands(true);
|
|
||||||
let cli_part = CliPart {
|
|
||||||
mize: mize.clone(),
|
|
||||||
cmd: Some(command),
|
|
||||||
actions: HashMap::new(),
|
|
||||||
parsers: Some(Vec::new()),
|
|
||||||
};
|
|
||||||
|
|
||||||
mize.add_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 = match matches.subcommand_name() {
|
|
||||||
Some(sub_cmd) => sub_cmd,
|
|
||||||
None => {
|
|
||||||
println!("No subcommand provided");
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let action = match self.actions.remove(sub_cmd) {
|
|
||||||
Some(action) => action,
|
|
||||||
None => {
|
|
||||||
// external parsers
|
|
||||||
for parser in self.parsers.take().unwrap() {
|
|
||||||
parser(
|
|
||||||
mize.clone(),
|
|
||||||
matches
|
|
||||||
.subcommand_matches(sub_cmd)
|
|
||||||
.unwrap()
|
|
||||||
.get_many::<String>("")
|
|
||||||
.unwrap()
|
|
||||||
.map(|s| s.to_string())
|
|
||||||
.collect::<Vec<String>>(),
|
|
||||||
)?;
|
|
||||||
}
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
action(matches.subcommand_matches(sub_cmd).unwrap(), mize.clone())?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
fn opts(&self, mize: &mut Mize) {
|
|
||||||
mize.new_opt("cli.name");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl CliPart {
|
|
||||||
pub fn subcommand<T: FnOnce(&ArgMatches, Mize) -> MizeResult<()> + Send + Sync + 'static>(
|
|
||||||
&mut self,
|
|
||||||
subcmd: Command,
|
|
||||||
action: T,
|
|
||||||
) -> &mut Command {
|
|
||||||
let name = subcmd.get_name().to_string();
|
|
||||||
let cmd = self.cmd.take().unwrap().subcommand(subcmd);
|
|
||||||
self.actions.insert(name, Box::new(action));
|
|
||||||
self.cmd = Some(cmd);
|
|
||||||
self.cmd.as_mut().unwrap()
|
|
||||||
}
|
|
||||||
pub fn add_sub_parser<
|
|
||||||
T: FnOnce(Mize, Vec<String>) -> MizeResult<()> + Send + Sync + 'static,
|
|
||||||
>(
|
|
||||||
&mut self,
|
|
||||||
parser: T,
|
|
||||||
) -> MizeResult<()> {
|
|
||||||
self.parsers.as_mut().unwrap().push(Box::new(parser));
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
pub fn with_cmd(
|
|
||||||
&mut self,
|
|
||||||
func: fn(mize: Mize, cmd: Command) -> MizeResult<Command>,
|
|
||||||
) -> MizeResult<()> {
|
|
||||||
let cmd = func(self.mize.clone(), self.cmd.take().unwrap())?;
|
|
||||||
self.cmd = Some(cmd);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "@ppc/marts",
|
|
||||||
"imports": {},
|
|
||||||
"exports": {
|
|
||||||
".": "./index.ts"
|
|
||||||
},
|
|
||||||
"tasks": {}
|
|
||||||
}
|
|
||||||
@@ -1,76 +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) {
|
|
||||||
Deno.core.print("hiiiiiiiiiiiiiiiiiiii from habitica");
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
habitica(globalThis.mize);
|
|
||||||
Deno.core.print("hiiiiiiiiiiiiiiiiiiii from habitica one");
|
|
||||||
})();
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
import * as esbuild from "npm:esbuild";
|
|
||||||
|
|
||||||
import { denoPlugin } from "jsr:@deno/esbuild-plugin";
|
|
||||||
|
|
||||||
await esbuild.build({
|
|
||||||
plugins: [denoPlugin()],
|
|
||||||
entryPoints: ["./habitica.ts"],
|
|
||||||
outfile: "./deno_dist/habitica.js",
|
|
||||||
bundle: true,
|
|
||||||
format: "iife",
|
|
||||||
});
|
|
||||||
esbuild.stop();
|
|
||||||
@@ -1,127 +0,0 @@
|
|||||||
use chrono::{DateTime, Utc};
|
|
||||||
use mize::{mize_err, mize_part, Mize, MizeError, MizePart, MizeResult};
|
|
||||||
use reqwest::{blocking::Client, blocking::Response, header, Method};
|
|
||||||
use serde_json::{json, Value};
|
|
||||||
use std::thread::sleep;
|
|
||||||
use std::time::Duration;
|
|
||||||
|
|
||||||
#[mize_part]
|
|
||||||
#[derive(Default)]
|
|
||||||
pub struct Habitica {
|
|
||||||
mize: Mize,
|
|
||||||
client: Client,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn habitica(mize: &mut Mize) -> MizeResult<()> {
|
|
||||||
let client = Client::new();
|
|
||||||
mize.register_part(Box::new(Habitica {
|
|
||||||
mize: mize.clone(),
|
|
||||||
client,
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
impl MizePart for Habitica {
|
|
||||||
fn opts(&self, mize: &mut Mize) {
|
|
||||||
mize.new_opt("habitica.api_url");
|
|
||||||
mize.new_opt("habitica.user_id");
|
|
||||||
mize.new_opt("habitica.api_token");
|
|
||||||
mize.new_opt("habitica.client_name");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Habitica {
|
|
||||||
pub fn api_request(&mut self, method: Method, path: String, data: Value) -> MizeResult<Value> {
|
|
||||||
let api_url = self.mize.get_config("habitica.api_url")?.to_string();
|
|
||||||
let user_id = self.mize.get_config("habitica.user_id")?.to_string();
|
|
||||||
let api_token = self.mize.get_config("habitica.api_token")?.to_string();
|
|
||||||
let client_name = self.mize.get_config("habitica.client_name")?.to_string();
|
|
||||||
|
|
||||||
let mut headers = header::HeaderMap::new();
|
|
||||||
headers.insert(
|
|
||||||
"Content-Type",
|
|
||||||
header::HeaderValue::from_static("application/json"),
|
|
||||||
);
|
|
||||||
headers.insert("x-api-user", header::HeaderValue::from_str(&user_id)?);
|
|
||||||
headers.insert("x-api-key", header::HeaderValue::from_str(&api_token)?);
|
|
||||||
headers.insert("x-client", header::HeaderValue::from_str(&client_name)?);
|
|
||||||
|
|
||||||
let url = format!("{}/{}", api_url, path.trim_start_matches('/'));
|
|
||||||
|
|
||||||
let mut request_builder = self.client.request(method, &url).headers(headers);
|
|
||||||
|
|
||||||
if data != json!({}) {
|
|
||||||
request_builder = request_builder.json(&data);
|
|
||||||
}
|
|
||||||
|
|
||||||
let response = request_builder.send()?;
|
|
||||||
|
|
||||||
handle_rate_limit(&response);
|
|
||||||
|
|
||||||
if !response.status().is_success() {
|
|
||||||
let status = response.status();
|
|
||||||
let text = response.text().unwrap_or_default();
|
|
||||||
println!("request_url: {}", url);
|
|
||||||
return Err(mize_err!(
|
|
||||||
"Habitica API error: {} {} - {}",
|
|
||||||
status.as_u16(),
|
|
||||||
status.canonical_reason().unwrap_or(""),
|
|
||||||
text
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
let json_response: Value = response.json()?;
|
|
||||||
Ok(json_response.get("data").cloned().unwrap_or(Value::Null))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_tasks(&mut self, task_type: &str) -> MizeResult<Value> {
|
|
||||||
self.api_request(
|
|
||||||
Method::GET,
|
|
||||||
format!("tasks/user?type={}", task_type),
|
|
||||||
json!({}),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn delete_task(&mut self, id: &str) -> MizeResult<Value> {
|
|
||||||
self.api_request(Method::DELETE, format!("tasks/{}", id), json!({}))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn handle_rate_limit(response: &Response) {
|
|
||||||
let headers = response.headers();
|
|
||||||
let limit = headers
|
|
||||||
.get("X-RateLimit-Limit")
|
|
||||||
.and_then(|v| v.to_str().ok())
|
|
||||||
.unwrap_or("NONE");
|
|
||||||
let remaining = headers
|
|
||||||
.get("X-RateLimit-Remaining")
|
|
||||||
.and_then(|v| v.to_str().ok())
|
|
||||||
.and_then(|s| s.parse::<i32>().ok())
|
|
||||||
.unwrap_or(10);
|
|
||||||
let reset = headers
|
|
||||||
.get("X-RateLimit-Reset")
|
|
||||||
.and_then(|v| v.to_str().ok());
|
|
||||||
|
|
||||||
println!(
|
|
||||||
"RateLimit: {} | Remaining: {} | Reset: {}",
|
|
||||||
limit,
|
|
||||||
remaining,
|
|
||||||
reset.unwrap_or("N/A")
|
|
||||||
);
|
|
||||||
|
|
||||||
if remaining < 2 {
|
|
||||||
if let Some(reset_str) = reset {
|
|
||||||
if let Ok(reset_date) = DateTime::parse_from_rfc2822(reset_str) {
|
|
||||||
let now = Utc::now();
|
|
||||||
let wait_duration = reset_date.signed_duration_since(now).to_std();
|
|
||||||
if let Ok(wait_duration) = wait_duration {
|
|
||||||
let wait_ms = wait_duration.as_millis() as u64 + 1000;
|
|
||||||
println!(
|
|
||||||
"Waiting {} secs for next rate limit window...",
|
|
||||||
(wait_ms / 1000)
|
|
||||||
);
|
|
||||||
sleep(Duration::from_millis(wait_ms));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
export * from "./task/obsidian-otask-parse";
|
|
||||||
export * from "./c2vi/obsidian-canvas-patch";
|
|
||||||
|
|
||||||
export * from "./js/stub_deno";
|
|
||||||
@@ -1,53 +0,0 @@
|
|||||||
use deno_core::extension;
|
|
||||||
use deno_core::op2;
|
|
||||||
use deno_core::OpState;
|
|
||||||
use mize::{Mize, MizeError};
|
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
use crate::CliPart;
|
|
||||||
use crate::JsPart;
|
|
||||||
|
|
||||||
#[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);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[op2(fast)]
|
|
||||||
fn op_cli_subcommand(state: &mut OpState, #[string] name: String) {
|
|
||||||
let mut mize: Mize = state.borrow::<Mize>().clone();
|
|
||||||
let mut cli = mize.get_part_native::<CliPart>("cli").unwrap();
|
|
||||||
cli.subcommand(
|
|
||||||
clap::Command::new(name.clone()),
|
|
||||||
move |_sub_matches, mut mize| {
|
|
||||||
let mut js = mize.get_part_native::<JsPart>("js").unwrap();
|
|
||||||
js.eval("log(\"cli command being called\")".to_string())?;
|
|
||||||
js.eval(format!("mize.vars.cli.subcommand({name})"))?;
|
|
||||||
Ok(())
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
import { Mize } from "./stub_deno.ts";
|
|
||||||
|
|
||||||
function argsToMessage(...args) {
|
|
||||||
return args.map((arg) => JSON.stringify(arg)).join(" ");
|
|
||||||
}
|
|
||||||
|
|
||||||
globalThis.log = (...args) => {
|
|
||||||
Deno.core.print(`[out]: ${argsToMessage(...args)}\n`, false);
|
|
||||||
};
|
|
||||||
|
|
||||||
globalThis.mize = new Mize();
|
|
||||||
@@ -1,136 +0,0 @@
|
|||||||
use deno_core::error::AnyError;
|
|
||||||
use deno_core::url::Url;
|
|
||||||
use deno_core::v8::OneByteConst;
|
|
||||||
use deno_core::{
|
|
||||||
ascii_str_include, 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,
|
|
||||||
sender: Option<flume::Sender<JsRuntimeThreadMessage>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
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 run(&mut self, mize: &mut Mize) -> MizeResult<()> {
|
|
||||||
println!("js part run");
|
|
||||||
self.sender
|
|
||||||
.as_ref()
|
|
||||||
.unwrap()
|
|
||||||
.send(JsRuntimeThreadMessage::DoRunPhase)
|
|
||||||
.unwrap();
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn js(mize: &mut Mize) -> MizeResult<()> {
|
|
||||||
let (sender, receiver) = flume::unbounded::<JsRuntimeThreadMessage>();
|
|
||||||
let mize_clone = mize.clone();
|
|
||||||
|
|
||||||
// the thread which will run any js
|
|
||||||
mize.spawn_and_wait("js_runtime_thread", || {
|
|
||||||
js_runtime_thread(mize_clone, receiver)
|
|
||||||
})?;
|
|
||||||
|
|
||||||
mize.add_part(Box::new(JsPart {
|
|
||||||
mize: mize.clone(),
|
|
||||||
sender: Some(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.execute_init_js(code)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
impl JsPart {
|
|
||||||
pub fn execute_init_js(&mut self, code: FastStaticString) -> MizeResult<()> {
|
|
||||||
self.sender
|
|
||||||
.as_ref()
|
|
||||||
.unwrap()
|
|
||||||
.send(JsRuntimeThreadMessage::RunInitJs(code))
|
|
||||||
.unwrap();
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
pub fn eval(&mut self, code: String) -> MizeResult<()> {
|
|
||||||
self.sender
|
|
||||||
.as_ref()
|
|
||||||
.unwrap()
|
|
||||||
.send(JsRuntimeThreadMessage::RunJs(code))
|
|
||||||
.unwrap();
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn js_runtime_thread(
|
|
||||||
mize: Mize,
|
|
||||||
receiver: flume::Receiver<JsRuntimeThreadMessage>,
|
|
||||||
) -> 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()
|
|
||||||
});
|
|
||||||
|
|
||||||
js_runtime
|
|
||||||
.execute_script("[stub]", ascii_str_include!("../deno_dist/glue_deno.js"))
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
loop {
|
|
||||||
println!("js thread waiting for smth");
|
|
||||||
let msg = receiver.recv().unwrap();
|
|
||||||
println!("js thread got smth");
|
|
||||||
match msg {
|
|
||||||
JsRuntimeThreadMessage::RunInitJs(js_code) => {
|
|
||||||
if let Err(err) = js_runtime.execute_script("[init]", js_code) {
|
|
||||||
println!("err: {}", err);
|
|
||||||
}
|
|
||||||
println!("done running js");
|
|
||||||
}
|
|
||||||
JsRuntimeThreadMessage::RunJs(js_code) => {
|
|
||||||
if let Err(err) = js_runtime.execute_script("[idk]", js_code) {
|
|
||||||
println!("err: {}", err);
|
|
||||||
}
|
|
||||||
println!("done running js");
|
|
||||||
}
|
|
||||||
JsRuntimeThreadMessage::DoRunPhase => {
|
|
||||||
if let Err(err) = js_runtime.execute_script("[runPhase]", "mize.runPhase()") {
|
|
||||||
println!("err: {}", err);
|
|
||||||
}
|
|
||||||
println!("done with runPhase");
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
enum JsRuntimeThreadMessage {
|
|
||||||
//Closure(Box<dyn FnOnce(&mut JsRuntime) -> MizeResult<()> + Send>),
|
|
||||||
//AsyncClosure(BoxClosure),
|
|
||||||
RunInitJs(FastStaticString),
|
|
||||||
RunJs(String),
|
|
||||||
DoRunPhase,
|
|
||||||
}
|
|
||||||
@@ -1,18 +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) {}
|
|
||||||
runPhase() {
|
|
||||||
Deno.core.print("runPhase in js \n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type MizeError = string;
|
|
||||||
|
|
||||||
export class Cli {
|
|
||||||
constructor(mize: Mize) {}
|
|
||||||
async sub_command(name: string, cmd: any): Promise<void> {}
|
|
||||||
}
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
|
|
||||||
use mize::MizeResult;
|
|
||||||
|
|
||||||
#[cfg(feature = "target-os")]
|
|
||||||
mod cli;
|
|
||||||
#[cfg(feature = "target-os")]
|
|
||||||
pub use cli::*;
|
|
||||||
|
|
||||||
#[cfg(feature = "target-os")]
|
|
||||||
pub mod habitica;
|
|
||||||
#[cfg(feature = "target-os")]
|
|
||||||
pub use habitica::*;
|
|
||||||
|
|
||||||
#[cfg(feature = "target-os")]
|
|
||||||
pub mod c2vi;
|
|
||||||
#[cfg(feature = "target-os")]
|
|
||||||
pub use c2vi::*;
|
|
||||||
|
|
||||||
//#[cfg(feature = "target-os")]
|
|
||||||
//pub mod js;
|
|
||||||
//#[cfg(feature = "target-os")]
|
|
||||||
//pub use js::*;
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "@ppc/marts",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"keywords": [],
|
|
||||||
"author": "",
|
|
||||||
"license": "ISC",
|
|
||||||
"dependencies": {
|
|
||||||
"@flowershow/remark-wiki-link": "^3.4.0",
|
|
||||||
"builtin-modules": "^5.0.0",
|
|
||||||
"esbuild": "^0.27.3",
|
|
||||||
"mdast": "^3.0.0",
|
|
||||||
"obsidian": "^1.12.3",
|
|
||||||
"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"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,538 +0,0 @@
|
|||||||
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";
|
|
||||||
import { TFile } from "obsidian";
|
|
||||||
|
|
||||||
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: any = [];
|
|
||||||
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) {
|
|
||||||
try {
|
|
||||||
// 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;
|
|
||||||
} catch (error) {
|
|
||||||
console.log("failed to parse list item:", listItem);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function otasks_from_file(file: TFile, otask_path, priority = 0) {
|
|
||||||
try {
|
|
||||||
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;
|
|
||||||
} catch (error) {
|
|
||||||
console.log("failed to parse file: ", file.path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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
2886
packages/mize/Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -1,5 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "@ppc/mize",
|
|
||||||
"imports": {},
|
|
||||||
"exports": "./src/platform/js/index.ts"
|
|
||||||
}
|
|
||||||
@@ -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
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
|
|
||||||
nix develop .#mizeFor.x86_64-linux-gnu.main.devShell --override-input nixpkgs github:NixOS/nixpkgs/ea6033ce4ddf2c0ede2ea147a450d4f880796128
|
|
||||||
@@ -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 "$@"
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,82 +0,0 @@
|
|||||||
use crate::mize_err;
|
|
||||||
use crate::MizeResult;
|
|
||||||
use std::collections::HashMap;
|
|
||||||
use std::env;
|
|
||||||
use std::sync::{Arc, RwLock};
|
|
||||||
|
|
||||||
use crate::item::ItemData;
|
|
||||||
use crate::Mize;
|
|
||||||
use crate::MizeError;
|
|
||||||
|
|
||||||
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());
|
|
||||||
}
|
|
||||||
|
|
||||||
// populate values from config files
|
|
||||||
let config_file_paths = 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())?;
|
|
||||||
println!("config data: {data}");
|
|
||||||
for path in data.get_paths_recursive()? {
|
|
||||||
let mut conf_name = path.replace("/", ".");
|
|
||||||
if conf_name.starts_with(".") {
|
|
||||||
conf_name.remove(0);
|
|
||||||
}
|
|
||||||
println!("adding config {conf_name} from config file {config_file_path}");
|
|
||||||
println!("path: {:?}", path);
|
|
||||||
let val = data.get_path(path.split("/").filter(|s| s != &"").collect::<Vec<&str>>())?;
|
|
||||||
match config_opts.get_mut(&conf_name) {
|
|
||||||
Some(opt) => {
|
|
||||||
opt.val = Some(val);
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
config_opts.insert(
|
|
||||||
conf_name.clone(),
|
|
||||||
ConfigOpt {
|
|
||||||
name: conf_name,
|
|
||||||
val: Some(val),
|
|
||||||
thunk: None,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
@@ -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 {})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,768 +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 to_toml(&self) -> MizeResult<String> {
|
|
||||||
let mut out_string = String::new();
|
|
||||||
let toml_serializer = toml::Serializer::new(&mut out_string);
|
|
||||||
CborValue::serialize(&self.cbor(), toml_serializer)?;
|
|
||||||
Ok(out_string)
|
|
||||||
}
|
|
||||||
|
|
||||||
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 value_bool(self) -> MizeResult<bool> {
|
|
||||||
match self.cbor() {
|
|
||||||
CborValue::Bool(val) => Ok(*val),
|
|
||||||
_ => 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())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_paths_recursive(&mut self) -> MizeResult<Vec<String>> {
|
|
||||||
let mut paths = Vec::new();
|
|
||||||
let mut path_accu = Vec::new();
|
|
||||||
get_paths(&mut self.0, &mut paths, &mut path_accu, true)?;
|
|
||||||
Ok(paths)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_paths(&mut self) -> MizeResult<Vec<String>> {
|
|
||||||
let mut paths = Vec::new();
|
|
||||||
let mut path_accu = Vec::new();
|
|
||||||
get_paths(&mut self.0, &mut paths, &mut path_accu, false)?;
|
|
||||||
Ok(paths)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_paths(
|
|
||||||
val: &mut CborValue,
|
|
||||||
paths: &mut Vec<String>,
|
|
||||||
path_accu: &mut Vec<String>,
|
|
||||||
recurse: bool,
|
|
||||||
) -> MizeResult<()> {
|
|
||||||
let map = val
|
|
||||||
.as_map_mut()
|
|
||||||
.ok_or(mize_err!("ItemData is not map... when calling get_paths"))?;
|
|
||||||
for (key, val) in map {
|
|
||||||
let text = key
|
|
||||||
.as_text()
|
|
||||||
.ok_or(mize_err!("ItemData::get_paths but key is not of type text"))?
|
|
||||||
.to_owned();
|
|
||||||
if recurse && val.is_map() {
|
|
||||||
let mut path_accu = path_accu.clone();
|
|
||||||
path_accu.push(text);
|
|
||||||
get_paths(val, paths, &mut path_accu, recurse)?;
|
|
||||||
} else {
|
|
||||||
let path = path_accu.join("/") + "/" + text.as_str();
|
|
||||||
paths.push(path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
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 {}
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
|
|
||||||
#![ allow( warnings ) ]
|
|
||||||
|
|
||||||
@@ -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();
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -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 = "target-js")]
|
|
||||||
pub mod js;
|
|
||||||
|
|
||||||
#[cfg(feature = "target-os")]
|
|
||||||
pub mod os;
|
|
||||||
|
|
||||||
pub mod any {
|
|
||||||
//////////// instance_init
|
|
||||||
#[cfg(feature = "target-js")]
|
|
||||||
pub use super::js::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-js")))]
|
|
||||||
pub use super::super::instance_init;
|
|
||||||
|
|
||||||
//////////// load_module
|
|
||||||
#[cfg(feature = "target-os")]
|
|
||||||
pub use super::os::load_module;
|
|
||||||
|
|
||||||
#[cfg(feature = "target-js")]
|
|
||||||
pub use super::js::load_module;
|
|
||||||
|
|
||||||
#[cfg(not(any(feature = "target-os", feature = "target-js")))]
|
|
||||||
pub use super::super::load_module;
|
|
||||||
|
|
||||||
//////////// fetch_module
|
|
||||||
#[cfg(feature = "target-os")]
|
|
||||||
pub use super::os::fetch_module;
|
|
||||||
|
|
||||||
#[cfg(feature = "target-js")]
|
|
||||||
pub use super::super::fetch_module;
|
|
||||||
|
|
||||||
#[cfg(not(any(feature = "target-os", feature = "target-js")))]
|
|
||||||
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())
|
|
||||||
}
|
|
||||||
@@ -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(())
|
|
||||||
}
|
|
||||||
@@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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();
|
|
||||||
}
|
|
||||||
@@ -1,368 +0,0 @@
|
|||||||
use ciborium::Value as CborValue;
|
|
||||||
use clap::ArgMatches;
|
|
||||||
use flate2::bufread::GzDecoder;
|
|
||||||
use serde::Deserialize;
|
|
||||||
use sha2::{Digest, Sha256, Sha512};
|
|
||||||
use std::env::VarError;
|
|
||||||
use std::fs;
|
|
||||||
use std::fs::File;
|
|
||||||
use std::io::copy;
|
|
||||||
use std::path::{Path, PathBuf};
|
|
||||||
use std::time::UNIX_EPOCH;
|
|
||||||
use tar::Archive;
|
|
||||||
use tracing::{debug, error, info, trace, warn};
|
|
||||||
|
|
||||||
use crate::error::{IntoMizeResult, MizeError, MizeResult};
|
|
||||||
use crate::id::MizeId;
|
|
||||||
use crate::instance::module::EmptyModule;
|
|
||||||
use crate::instance::store::Store;
|
|
||||||
use crate::instance::Mize;
|
|
||||||
use crate::item::{data_from_string, IntoItemData, ItemData};
|
|
||||||
use crate::memstore::MemStore;
|
|
||||||
use crate::{mize_err, Module};
|
|
||||||
|
|
||||||
use self::fsstore::FileStore;
|
|
||||||
|
|
||||||
pub mod fsstore;
|
|
||||||
pub mod logging;
|
|
||||||
|
|
||||||
#[cfg(target_family = "unix")]
|
|
||||||
mod unix_socket;
|
|
||||||
|
|
||||||
#[cfg(target_family = "unix")]
|
|
||||||
use crate::platform::os::unix_socket::UnixListener;
|
|
||||||
//mod web;
|
|
||||||
|
|
||||||
pub fn os_instance_init(instance: &mut Mize) -> MizeResult<()> {
|
|
||||||
// this is the code, that runs to initialize an Instance on a system with an os present.
|
|
||||||
|
|
||||||
////// load MIZE_CONFIG_FILE as the instance, which is item 0
|
|
||||||
match std::env::var("MIZE_CONFIG_FILE") {
|
|
||||||
Ok(config_file_path) => {
|
|
||||||
let config = config_from_file(config_file_path)?;
|
|
||||||
debug!("env var MIZE_CONFIG_FILE present");
|
|
||||||
instance.set_blocking("0", config)?;
|
|
||||||
trace!(
|
|
||||||
"config after MIZE_CONFIG_FILE env var: {}",
|
|
||||||
instance.get("self/config")?
|
|
||||||
);
|
|
||||||
}
|
|
||||||
Err(var_err) => match var_err {
|
|
||||||
VarError::NotPresent => {
|
|
||||||
debug!("env var MIZE_CONFIG_FILE NOT present");
|
|
||||||
}
|
|
||||||
VarError::NotUnicode(_) => {
|
|
||||||
warn!("env var MIZE_CONFIG_FILE is not Unicode, so not reading it")
|
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
////// load config from MIZE_CONFIG env var
|
|
||||||
match std::env::var("MIZE_CONFIG") {
|
|
||||||
Ok(config_string) => {
|
|
||||||
let config = data_from_string(config_string)?;
|
|
||||||
debug!("env var MIZE_CONFIG present");
|
|
||||||
instance.set_blocking("self/config", config)?;
|
|
||||||
trace!(
|
|
||||||
"config after MIZE_CONFIG env var: {}",
|
|
||||||
instance.get("self/config")?
|
|
||||||
);
|
|
||||||
}
|
|
||||||
Err(var_err) => match var_err {
|
|
||||||
VarError::NotPresent => {
|
|
||||||
debug!("env var MIZE_CONFIG NOT present")
|
|
||||||
}
|
|
||||||
VarError::NotUnicode(_) => {
|
|
||||||
warn!("env var MIZE_CONFIG is not Unicode, so not reading it")
|
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
////// if a config.store_path is set, upgrade to the filestore there
|
|
||||||
let mut test = instance.get("0")?.as_data_full()?;
|
|
||||||
let mut store_path = match instance.get("self/config/store_path")?.value_string() {
|
|
||||||
Ok(path) => path,
|
|
||||||
Err(e) => {
|
|
||||||
// the default store_path: $HOME/.mize
|
|
||||||
let home_dir = if let Some(dir) = std::env::home_dir() {
|
|
||||||
dir
|
|
||||||
} else {
|
|
||||||
return Err(mize_err!(
|
|
||||||
"could not get home_dir and store_path is not set in the config"
|
|
||||||
));
|
|
||||||
};
|
|
||||||
|
|
||||||
let store_path_buff = home_dir.join(".mize");
|
|
||||||
let store_path = store_path_buff.display().to_string();
|
|
||||||
|
|
||||||
instance.set_blocking(
|
|
||||||
"self/config/store_path",
|
|
||||||
store_path.clone().into_item_data(),
|
|
||||||
)?;
|
|
||||||
|
|
||||||
let store_path = instance.get("self/config/store_path")?.value_string()?;
|
|
||||||
|
|
||||||
println!("store_path: {}", store_path);
|
|
||||||
|
|
||||||
store_path
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if FileStore::store_is_opened(store_path.to_owned(), instance)? {
|
|
||||||
// if the store is already opened, connect to the instance, that opened it and join
|
|
||||||
// it's namespace
|
|
||||||
|
|
||||||
#[cfg(target_family = "unix")]
|
|
||||||
{
|
|
||||||
info!("CONNECTING");
|
|
||||||
use crate::platform::os::unix_socket;
|
|
||||||
unix_socket::connect(instance, store_path.into())?;
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(target_family = "windows")]
|
|
||||||
{
|
|
||||||
error!("CONNECTING... would connect, but that is not implemented on windows yet");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// else open it ourselves
|
|
||||||
let file_store = FileStore::new(store_path.as_str())?;
|
|
||||||
instance.migrate_to_store(Box::new(file_store))?;
|
|
||||||
|
|
||||||
let path = Path::new(&store_path).to_owned();
|
|
||||||
|
|
||||||
#[cfg(target_family = "unix")]
|
|
||||||
{
|
|
||||||
instance.add_listener(UnixListener::new(path)?)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(target_family = "windows")]
|
|
||||||
{
|
|
||||||
warn!("would add a Listener on a local socket, but that is not yet implemented for windows");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn seconds_since_modification(path: &Path) -> MizeResult<u64> {
|
|
||||||
let metadata = fs::metadata(path)?;
|
|
||||||
|
|
||||||
let modified = metadata.modified()?;
|
|
||||||
|
|
||||||
let duration = modified.duration_since(UNIX_EPOCH)?;
|
|
||||||
|
|
||||||
Ok(duration.as_secs())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_module_hash(
|
|
||||||
instance: &mut Mize,
|
|
||||||
name: &str,
|
|
||||||
mut options: ItemData,
|
|
||||||
) -> MizeResult<(String, String)> {
|
|
||||||
let mut selector_data = instance.get("self/config/selector")?.as_data_full()?;
|
|
||||||
|
|
||||||
// handle the case, when we want a module for not the system we are running on...
|
|
||||||
println!("get_module_hash: name: {}", name);
|
|
||||||
let mut name_parts = name.split(".");
|
|
||||||
if name_parts
|
|
||||||
.next()
|
|
||||||
.ok_or(mize_err!("no 0th element in the modName"))?
|
|
||||||
== "cross"
|
|
||||||
{
|
|
||||||
let system = name_parts
|
|
||||||
.next()
|
|
||||||
.ok_or(mize_err!("no 1th element in the modName"))?;
|
|
||||||
let mod_name: String = name_parts.collect();
|
|
||||||
println!("get_module_hash: mod_name: {}", mod_name);
|
|
||||||
if mod_name == "" {
|
|
||||||
return Err(mize_err!("no 2nd element in the modName"));
|
|
||||||
}
|
|
||||||
|
|
||||||
selector_data.set_path("system", system)?;
|
|
||||||
selector_data.set_path("modName", mod_name)?;
|
|
||||||
} else {
|
|
||||||
selector_data.set_path("modName", name)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
selector_data.merge(options);
|
|
||||||
|
|
||||||
// important to produce the same hash for the sama selector data
|
|
||||||
selector_data.sort_keys();
|
|
||||||
|
|
||||||
let selector_str = selector_data.to_json()?;
|
|
||||||
|
|
||||||
let mut hasher = Sha256::new();
|
|
||||||
|
|
||||||
hasher.update(selector_str.clone().as_bytes());
|
|
||||||
|
|
||||||
let result = hasher.finalize();
|
|
||||||
|
|
||||||
let mut hex: String = result
|
|
||||||
.iter()
|
|
||||||
.map(|b| format!("{:02x}", b).to_string())
|
|
||||||
.collect::<Vec<String>>()
|
|
||||||
.join("");
|
|
||||||
|
|
||||||
hex.truncate(32);
|
|
||||||
|
|
||||||
return Ok((hex, selector_str));
|
|
||||||
}
|
|
||||||
|
|
||||||
fn download_module(
|
|
||||||
instance: &mut Mize,
|
|
||||||
store_path: &str,
|
|
||||||
module_url: &str,
|
|
||||||
module_hash: &str,
|
|
||||||
module_name: &str,
|
|
||||||
module_selector: &str,
|
|
||||||
) -> MizeResult<()> {
|
|
||||||
let tmp_file_path_gz = format!("{}/{}.tar.gz", store_path, module_hash);
|
|
||||||
|
|
||||||
let tmp_gz_file = if !PathBuf::from(tmp_file_path_gz.clone()).exists() {
|
|
||||||
let mut tmp_gz_file = File::create(&tmp_file_path_gz)?;
|
|
||||||
|
|
||||||
debug!(
|
|
||||||
"downloading module '{}' with hash: {}",
|
|
||||||
module_name, module_hash
|
|
||||||
);
|
|
||||||
|
|
||||||
let response = http_req::request::get(
|
|
||||||
format!(
|
|
||||||
"http://{}/mize/dist/{}-{}.tar.gz",
|
|
||||||
module_url, module_hash, module_name
|
|
||||||
),
|
|
||||||
&mut tmp_gz_file,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
let code = response.status_code();
|
|
||||||
if code != 200.into() {
|
|
||||||
return Err(mize_err!(
|
|
||||||
"failed to get module '{}' with hash '{}' and selector: {}",
|
|
||||||
module_name,
|
|
||||||
module_hash,
|
|
||||||
module_selector
|
|
||||||
)
|
|
||||||
.msg(format!(
|
|
||||||
"the http request to fetch the module returned a status code of: {}",
|
|
||||||
code
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
|
|
||||||
tmp_gz_file
|
|
||||||
} else {
|
|
||||||
std::fs::File::open(tmp_file_path_gz.clone())?
|
|
||||||
};
|
|
||||||
|
|
||||||
let tar = GzDecoder::new(std::io::BufReader::new(tmp_gz_file));
|
|
||||||
let mut archive = Archive::new(tar);
|
|
||||||
|
|
||||||
//archive.set_mask(umask::Mode::parse("rwxrwxrwx")?.into());
|
|
||||||
|
|
||||||
let target_dir = format!("{}/modules/{}", store_path, module_hash);
|
|
||||||
|
|
||||||
std::fs::create_dir_all(target_dir.as_str())?;
|
|
||||||
|
|
||||||
archive.unpack(target_dir.as_str())?;
|
|
||||||
|
|
||||||
fs::remove_file(&tmp_file_path_gz)?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn fetch_module(instance: &mut Mize, module_name: &str) -> MizeResult<String> {
|
|
||||||
let module_name_with_slashes = module_name.replace(".", "/");
|
|
||||||
if let Ok(module_path) = instance
|
|
||||||
.get(format!(
|
|
||||||
"self/config/module_dir/{}",
|
|
||||||
module_name_with_slashes
|
|
||||||
))?
|
|
||||||
.value_string()
|
|
||||||
{
|
|
||||||
// in case a dir is configured, which holds the modules output
|
|
||||||
return Ok(module_path);
|
|
||||||
} else {
|
|
||||||
// if null, load it from the url
|
|
||||||
let module_url = instance.get("self/config/module_url")?.value_string()?;
|
|
||||||
|
|
||||||
let store_path = instance.get("self/config/store_path")?.value_string()?;
|
|
||||||
|
|
||||||
let (module_hash, module_selector) =
|
|
||||||
get_module_hash(instance, module_name, ItemData::new())?;
|
|
||||||
|
|
||||||
if !PathBuf::from(format!("{}/modules/{}", store_path, module_hash.as_str())).exists() {
|
|
||||||
// download module if it does not exist
|
|
||||||
download_module(
|
|
||||||
instance,
|
|
||||||
store_path.as_str(),
|
|
||||||
module_url.as_str(),
|
|
||||||
module_hash.as_str(),
|
|
||||||
module_name,
|
|
||||||
module_selector.as_str(),
|
|
||||||
)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Ok(format!("{}/modules/{}", store_path, module_hash.as_str()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn load_module(instance: &mut Mize, module_name: &str, path: Option<String>) -> MizeResult<()> {
|
|
||||||
let module_path = if path.is_some() {
|
|
||||||
format!("{}/lib/libmize_module_{}.so", path.unwrap(), module_name)
|
|
||||||
} else {
|
|
||||||
format!(
|
|
||||||
"{}/lib/libmize_module_{}.so",
|
|
||||||
fetch_module(instance, module_name)?,
|
|
||||||
module_name
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
let lib = unsafe { libloading::Library::new(module_path)? };
|
|
||||||
|
|
||||||
let func: libloading::Symbol<
|
|
||||||
unsafe extern "C" fn(&mut Box<dyn Module + Send + Sync>, Mize) -> (),
|
|
||||||
> = unsafe { lib.get(format!("get_mize_module_{}", module_name).as_bytes())? };
|
|
||||||
|
|
||||||
let mut module: Box<dyn Module + Send + Sync> = Box::new(EmptyModule {});
|
|
||||||
|
|
||||||
unsafe { func(&mut module, instance.clone()) };
|
|
||||||
|
|
||||||
let mut modules_inner = instance.modules.lock()?;
|
|
||||||
|
|
||||||
module.init(&instance)?;
|
|
||||||
|
|
||||||
modules_inner.insert(module_name.to_owned(), module);
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
// dropping the lib, would (i suspect) free all memory, of the library's code, which would
|
|
||||||
// make the module's vtable point into empty memory
|
|
||||||
std::mem::forget(lib);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn config_from_cli_args(matches: &ArgMatches) -> MizeResult<ItemData> {
|
|
||||||
let mut config = ItemData::new();
|
|
||||||
|
|
||||||
if let Some(config_file_path) = matches.get_one::<String>("config-file") {
|
|
||||||
config.merge(config_from_file(config_file_path.to_string())?);
|
|
||||||
trace!("config after --config-file arg: {}", config);
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(config_string) = matches.get_one::<String>("config") {
|
|
||||||
config.merge(data_from_string(config_string.to_string())?);
|
|
||||||
trace!("config after --config arg: {}", config);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Ok(config);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn config_from_file(file_path: String) -> MizeResult<ItemData> {
|
|
||||||
let toml_string = fs::read_to_string(&file_path)
|
|
||||||
.mize_result_msg(format!("Could not read file {} as mize config", &file_path))?;
|
|
||||||
|
|
||||||
let item_data = ItemData::from_toml(toml_string.as_str())?;
|
|
||||||
|
|
||||||
return Ok(item_data);
|
|
||||||
}
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user