From f9e78782235ae8c85133b698aa5935c4bec0baca Mon Sep 17 00:00:00 2001 From: NotBioWaste905 Date: Sun, 8 Jun 2025 23:48:51 +0300 Subject: [PATCH 1/6] Add neo4j database handlers and remove old task persistence --- TODO.md | 2 +- meshwork/backend/app.py | 52 +++++++++++++--- meshwork/backend/core/db_handler.py | 87 ++++++++++++++++++++++++++ meshwork/backend/core/graph.py | 95 ++++++++++++++--------------- meshwork/backend/core/task.py | 1 + meshwork/backend/core/user.py | 1 + meshwork/backend/pyproject.toml | 1 + meshwork/backend/uv.lock | 23 +++++++ 8 files changed, 202 insertions(+), 60 deletions(-) create mode 100644 meshwork/backend/core/db_handler.py diff --git a/TODO.md b/TODO.md index 28782bc..6b20060 100644 --- a/TODO.md +++ b/TODO.md @@ -3,6 +3,6 @@ - [x] frontend and backend API connection - [x] node connection - [x] task editing -- [ ] task layout +- [x] task layout - [ ] task graph creation (multigraph support/auth) - [ ] move to neo4j diff --git a/meshwork/backend/app.py b/meshwork/backend/app.py index 6ed7fc3..81a4e8e 100644 --- a/meshwork/backend/app.py +++ b/meshwork/backend/app.py @@ -11,28 +11,48 @@ logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) + def setup_examples() -> tuple[TaskGraph, User]: """Initialize example tasks and users""" example_graph = TaskGraph() example_graph.id = "abc123" - user = User(id=123, name="John Doe", email="john@example.com", graphs=[example_graph.id]) - task_1 = Task(name="Create pretty task nodes", description="Learn UI and do it!", status=Status.TODO, tags=["UI", "UX"], users=["Andrew"]) + user = User( + id=123, name="John Doe", email="john@example.com", graphs=[example_graph.id] + ) + task_1 = Task( + name="Create pretty task nodes", + description="Learn UI and do it!", + status=Status.TODO, + tags=["UI", "UX"], + users=["Andrew"], + ) logger.info(f"Created task {task_1.id}") - task_2 = Task(name="Add CI/CD support", description="Things to test your Svelte", status=Status.TODO, tags=["CI/CD"], users=["Andrew"]) + task_2 = Task( + name="Add CI/CD support", + description="Things to test your Svelte", + status=Status.TODO, + tags=["CI/CD"], + users=["Andrew"], + ) logger.info(f"Created task {task_2.id}") - task_3 = Task(name="Release v0.1", description="Hooray!", status=Status.TODO, tags=["v0.1"], depends_on=[task_1.id, task_2.id]) + task_3 = Task( + name="Release v0.1", + description="Hooray!", + status=Status.TODO, + tags=["v0.1"], + depends_on=[task_1.id, task_2.id], + ) logger.info(f"Created task {task_3.id}") example_graph.add_task(task_1) example_graph.add_task(task_2) example_graph.add_task(task_3) return example_graph, user + app = FastAPI() TG, user = setup_examples() logger.info(f"Initialized graph {TG.id}") -task_graphs = { - TG.id: TG -} +task_graphs = {TG.id: TG} # Configure CORS app.add_middleware( @@ -43,10 +63,12 @@ def setup_examples() -> tuple[TaskGraph, User]: allow_headers=["*"], ) + class ConnectNodesRequest(BaseModel): node_dependency: str node_dependee: str + @app.get("/") async def root(): return {"message": "Hello World"} @@ -63,6 +85,7 @@ async def create_graph(): task_graphs[new_graph.id] = new_graph return {"ID": new_graph.id} + @app.post("/v0/{graph_id}/task/add/") async def add_task(graph_id: str, task: Task): task_graphs[graph_id].add_task(task) @@ -73,6 +96,7 @@ async def add_task(graph_id: str, task: Task): async def get_all_tasks(graph_id: str) -> list[Task]: return task_graphs[graph_id].get_all_tasks() + @app.get("/v0/{graph_id}/task/{task_id}") async def get_task(graph_id: str, task_id: str) -> Task: return task_graphs[graph_id].get_task(task_id) @@ -89,12 +113,20 @@ async def edit_task(graph_id: str, task_id: str, new_task: Task): task_graphs[graph_id].edit_task(task_id, new_task) return {"message": f"Task {task_id} edited"} + @app.post("/v0/{graph_id}/connect_nodes/") async def connect_nodes(graph_id: str, request: ConnectNodesRequest): task_graphs[graph_id].connect_nodes(request.node_dependency, request.node_dependee) - return {"message": f"Nodes {request.node_dependency} and {request.node_dependee} connected"} + return { + "message": f"Nodes {request.node_dependency} and {request.node_dependee} connected" + } + @app.post("/v0/{graph_id}/disconnect_nodes/") async def disconnect_nodes(graph_id: str, request: ConnectNodesRequest): - task_graphs[graph_id].disconnect_nodes(request.node_dependency, request.node_dependee) - return {"message": f"Nodes {request.node_dependency} and {request.node_dependee} disconnected"} + task_graphs[graph_id].disconnect_nodes( + request.node_dependency, request.node_dependee + ) + return { + "message": f"Nodes {request.node_dependency} and {request.node_dependee} disconnected" + } diff --git a/meshwork/backend/core/db_handler.py b/meshwork/backend/core/db_handler.py new file mode 100644 index 0000000..581ae8c --- /dev/null +++ b/meshwork/backend/core/db_handler.py @@ -0,0 +1,87 @@ +import os +import logging +from typing import Optional, List, Dict, Any +from contextlib import contextmanager + +from dotenv import load_dotenv +from neo4j import GraphDatabase, Driver, Session +from core.task import Task, Status +from core.user import User + +logging.basicConfig(level=logging.INFO) +logger = logging.getLogger(__name__) + + +class Neo4jHandler: + def __init__(self) -> None: + load_dotenv() + self.uri = os.getenv("NEO4J_URI") + self.auth = (os.getenv("NEO4J_USERNAME"), os.getenv("NEO4J_PASSWORD")) + + def connect(self): + self.driver = GraphDatabase.driver(self.uri, auth=self.auth) + self.driver.verify_connectivity() + logger.info("Connected to Neo4j backend.") + + def close(self): + if self.driver: + self.driver.close() + logger.info("Disconnected from Neo4j backend.") + + def create_example(self): + pass + + @contextmanager + def session(self): + with self.driver.session() as session: + yield session + + def create_user(self, user: User) -> str: + with self.session() as session: + try: + result = session.run( + """ + CREATE (u:User {id: $id, name: $name, email: $email, created_at: datetime()}) RETURN u.id + """, + id=user.id, + name=user.name, + email=user.email, + ) + return result.single()["u.id"] + except Exception as e: + logger.error(f"Failed to create user: {e}") + raise + + def create_task_graph(self, user_id: str, graph_name: str) -> str: + with self.session() as session: + try: + result = session.run( + """ + MATCH (u:User {id: $user_id}) + CREATE (tg:TaskGraph {id: randomUUID(), name: $graph_name, created_at: datetime()}) + CREATE (u)-[:OWNS]->(tg) + CREATE (u)-[:PART_OF]->(tg) + RETURN tg.id + """, + user_id=user_id, + graph_name=graph_name, + ) + return result.single()["tg.id"] + except Exception as e: + logger.error(f"Failed to create task graph: {e}") + raise + + def get_user_graphs(self, user_id: str) -> List[Dict[str, Any]]: + with self.session() as session: + try: + result = session.run( + """ + MATCH (u:User {id: $user_id})-[:PART_OF]->(tg:TaskGraph) + RETURN tg.id as id, tg.name as name, tg.created_at as created_at + """, + user_id=user_id, + ) + return [record.data() for record in result] + except Exception as e: + logger.error(f"Failed to get user graphs: {e}") + raise diff --git a/meshwork/backend/core/graph.py b/meshwork/backend/core/graph.py index 4d070f7..f059e80 100644 --- a/meshwork/backend/core/graph.py +++ b/meshwork/backend/core/graph.py @@ -10,6 +10,7 @@ from networkx import DiGraph from pydantic import BaseModel, Field +from core.db_handler import Neo4jHandler from core.task import Task, Status logger = logging.getLogger(__name__) @@ -21,31 +22,49 @@ class TaskGraph(BaseModel): id: str = Field(default=uuid.uuid4().hex) graph: DiGraph = Field(default_factory=DiGraph) name: str = Field(default_factory=str) - backup_interval: int = Field(default=10) - """Interval in seconds for automatic backup.""" - backup_folder: str = Field(default="backup") + db_handler: Neo4jHandler = Field(exclude=True) class Config: arbitrary_types_allowed = True def __init__(self, **data): super().__init__(**data) - if "backup_interval" in data: - self.backup_interval = data["backup_interval"] - schedule.every(self.backup_interval).seconds.do(self.backup_json) - self.id = uuid.uuid4().hex - - def load_backup(self): - # TODO: Implement loading the file with the most recent timestamp in filename - raise NotImplementedError("Backup loading is not implemented yet.") + if not hasattr(self, "db_handler"): + self.db_handler = Neo4jHandler() def add_task(self, task: Task): """Add a task to the graph.""" - self.graph.add_node(task.id, task=task) - logger.info(f"Added task {task.id} to the graph") - for d in task.depends_on: - self.graph.add_edge(d, task.id) - logger.info(f"Added dependency {d} -> {task.id}") + with self.db_handler.session() as session: + session.run( + """ + MATCH (tg:TaskGraph {id: $graph_id}) + CREATE (t:Task {id: $task_id, + name: $name, + description: $description, + status: $status, + tags: $tags, + users: $users, + created_at: datetime()}) + CREATE (tg)-[:CONTAINS]->(t) + """, + graph_id=self.id, + task_id=task.id, + name=task.name, + description=task.description, + status=task.status, + tags=task.tags, + users=task.users, + ) + + for dependency in task.depends_on: + session.run( + """ + MATCH (t1:Task {id: $task_id}), (t2:Task {id: $dep_id}) + CREATE (t1)-[:DEPENDS_ON]->(t2) + """, + task_id=task.id, + dep_id=dependency, + ) self.set_blocked_tasks() @@ -73,7 +92,6 @@ def connect_nodes(self, node_dependency: str, node_dependee: str): self.set_blocked_tasks() - def disconnect_nodes(self, node_dependency: str, node_dependee: str): """Disconnect two nodes in the graph.""" self.graph.remove_edge(node_dependency, node_dependee) @@ -82,20 +100,21 @@ def disconnect_nodes(self, node_dependency: str, node_dependee: str): self.set_blocked_tasks() - def set_blocked_tasks(self): """ Iterate over graph and set tasks as blocked if their dependencies are not DONE. """ - for node in self.graph.nodes: - task = self.graph.nodes[node]["task"] - if not all( - self.get_task(dep).status == Status.DONE for dep in task.depends_on - ): - task.status = Status.BLOCKED - else: - if task.status == Status.BLOCKED: - task.status = Status.TODO + # for node in self.graph.nodes: + # task = self.graph.nodes[node]["task"] + # if not all( + # self.get_task(dep).status == Status.DONE for dep in task.depends_on + # ): + # task.status = Status.BLOCKED + # else: + # if task.status == Status.BLOCKED: + # task.status = Status.TODO + with self.db_handler.session() as session: + pass def delete_task(self, task_id: str): """Delete a task from the graph.""" @@ -107,28 +126,6 @@ def delete_task(self, task_id: str): task.depends_on.remove(task_id) self.set_blocked_tasks() - def visualize(self): - import matplotlib.pyplot as plt - pos = nx.kamada_kawai_layout(self.graph) - nx.draw(self.graph, pos=pos, with_labels=True, arrows=True) - plt.show() # This line is crucial to display the figure - - def backup_json(self): - # Convert graph data to a serializable format - json_data = json_graph.node_link_data(self.graph) - - # Convert all Task objects to dicts and handle Status enum - for node in json_data["nodes"]: - if "task" in node: - task_dict = node["task"].dict() # Convert Pydantic model to dict - task_dict["status"] = task_dict["status"].value # Convert enum to value - node["task"] = task_dict - - filename = f"{datetime.datetime.now().strftime('%Y%m%d%H%M%S')}_{self.name}_backup.json" - with open(filename, "w") as f: - json.dump(json_data, f, indent=4) - logger.info(f"Backup created: {filename}") - # Example usage if __name__ == "__main__": diff --git a/meshwork/backend/core/task.py b/meshwork/backend/core/task.py index ade9870..3798c6e 100644 --- a/meshwork/backend/core/task.py +++ b/meshwork/backend/core/task.py @@ -33,6 +33,7 @@ class Task(BaseModel): """Task status""" graph_id: str = Field(default="") """Graph ID""" + # completion_condition: Optional[CompletionCondition] # """Condition that need to be met for the task to be completed""" def __init__(self, **data): diff --git a/meshwork/backend/core/user.py b/meshwork/backend/core/user.py index db40d11..426d353 100644 --- a/meshwork/backend/core/user.py +++ b/meshwork/backend/core/user.py @@ -1,5 +1,6 @@ from pydantic import BaseModel, Field + class User(BaseModel): id: int = Field(..., title="User ID") email: str = Field(..., title="Email Address") diff --git a/meshwork/backend/pyproject.toml b/meshwork/backend/pyproject.toml index f13d098..dbc7342 100644 --- a/meshwork/backend/pyproject.toml +++ b/meshwork/backend/pyproject.toml @@ -6,6 +6,7 @@ readme = "README.md" requires-python = ">=3.12" dependencies = [ "fastapi[all,standard]>=0.115.12", + "neo4j>=5.28.1", "networkx>=3.4.2", "pydantic>=2.11.5", "schedule>=1.2.2", diff --git a/meshwork/backend/uv.lock b/meshwork/backend/uv.lock index c4b6126..f684534 100644 --- a/meshwork/backend/uv.lock +++ b/meshwork/backend/uv.lock @@ -30,6 +30,7 @@ version = "0.1.0" source = { virtual = "." } dependencies = [ { name = "fastapi", extra = ["all", "standard"] }, + { name = "neo4j" }, { name = "networkx" }, { name = "pydantic" }, { name = "schedule" }, @@ -45,6 +46,7 @@ dev = [ [package.metadata] requires-dist = [ { name = "fastapi", extras = ["all", "standard"], specifier = ">=0.115.12" }, + { name = "neo4j", specifier = ">=5.28.1" }, { name = "networkx", specifier = ">=3.4.2" }, { name = "pydantic", specifier = ">=2.11.5" }, { name = "schedule", specifier = ">=1.2.2" }, @@ -475,6 +477,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979 }, ] +[[package]] +name = "neo4j" +version = "5.28.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pytz" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/4b/20/733dac16f7cedc80b23093415822c9763302519cba0e7c8bcdb5c01fc512/neo4j-5.28.1.tar.gz", hash = "sha256:ae8e37a1d895099062c75bc359b2cce62099baac7be768d0eba7180c1298e214", size = 231094 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6a/57/94225fe5e9dabdc0ff60c88cbfcedf11277f4b34e7ab1373d3e62dbdd207/neo4j-5.28.1-py3-none-any.whl", hash = "sha256:6755ef9e5f4e14b403aef1138fb6315b120631a0075c138b5ddb2a06b87b09fd", size = 312258 }, +] + [[package]] name = "networkx" version = "3.4.2" @@ -742,6 +756,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/45/58/38b5afbc1a800eeea951b9285d3912613f2603bdf897a4ab0f4bd7f405fc/python_multipart-0.0.20-py3-none-any.whl", hash = "sha256:8a62d3a8335e06589fe01f2a3e178cdcc632f3fbe0d492ad9ee0ec35aab1f104", size = 24546 }, ] +[[package]] +name = "pytz" +version = "2025.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f8/bf/abbd3cdfb8fbc7fb3d4d38d320f2441b1e7cbe29be4f23797b4a2b5d8aac/pytz-2025.2.tar.gz", hash = "sha256:360b9e3dbb49a209c21ad61809c7fb453643e048b38924c765813546746e81c3", size = 320884 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/81/c4/34e93fe5f5429d7570ec1fa436f1986fb1f00c3e0f43a589fe2bbcd22c3f/pytz-2025.2-py2.py3-none-any.whl", hash = "sha256:5ddf76296dd8c44c26eb8f4b6f35488f3ccbf6fbbd7adee0b7262d43f0ec2f00", size = 509225 }, +] + [[package]] name = "pyyaml" version = "6.0.2" From 82a292935cd7a0787b4d2793960c942623497b93 Mon Sep 17 00:00:00 2001 From: NotBioWaste905 Date: Tue, 22 Jul 2025 03:58:58 +0300 Subject: [PATCH 2/6] Casually rewriting the whole backend in Rust, added neo4j to the docker --- .gitignore | 3 + compose.yaml | 15 +- meshwork/backend_rs/Cargo.lock | 1718 ++++++++++++++++++++++++++++ meshwork/backend_rs/Cargo.toml | 18 + meshwork/backend_rs/Dockerfile | 7 + meshwork/backend_rs/src/main.rs | 187 +++ meshwork/backend_rs/src/schemas.rs | 22 + 7 files changed, 1969 insertions(+), 1 deletion(-) create mode 100644 meshwork/backend_rs/Cargo.lock create mode 100644 meshwork/backend_rs/Cargo.toml create mode 100644 meshwork/backend_rs/Dockerfile create mode 100644 meshwork/backend_rs/src/main.rs create mode 100644 meshwork/backend_rs/src/schemas.rs diff --git a/.gitignore b/.gitignore index ee8b505..2621fd6 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,6 @@ wheels/ .zed .ruff_cache .ropeproject + +# Rust +target diff --git a/compose.yaml b/compose.yaml index f96df30..39266b2 100644 --- a/compose.yaml +++ b/compose.yaml @@ -1,7 +1,7 @@ name: meshwork services: backend: - build: ./meshwork/backend + build: ./meshwork/backend_rs ports: - "8000:8000" frontend: @@ -10,6 +10,19 @@ services: - "3000:3000" depends_on: - backend + neo4j: + image: neo4j:latest + volumes: + - /$HOME/neo4j/logs:/logs + - /$HOME/neo4j/config:/config + - /$HOME/neo4j/data:/data + - /$HOME/neo4j/plugins:/plugins + environment: + - NEO4J_AUTH=neo4j/your_password + ports: + - "7474:7474" + - "7687:7687" + restart: always networks: default: diff --git a/meshwork/backend_rs/Cargo.lock b/meshwork/backend_rs/Cargo.lock new file mode 100644 index 0000000..e5163ca --- /dev/null +++ b/meshwork/backend_rs/Cargo.lock @@ -0,0 +1,1718 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "addr2line" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "async-trait" +version = "0.1.88" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e539d3fca749fcee5236ab05e93a52867dd549cc157c8cb7f99595f3cedffdb5" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + +[[package]] +name = "axum" +version = "0.6.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b829e4e32b91e643de6eafe82b1d90675f5874230191a4ffbc1b336dec4d6bf" +dependencies = [ + "async-trait", + "axum-core", + "bitflags 1.3.2", + "bytes", + "futures-util", + "http", + "http-body", + "hyper", + "itoa", + "matchit", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "rustversion", + "serde", + "serde_json", + "serde_path_to_error", + "serde_urlencoded", + "sync_wrapper", + "tokio", + "tower", + "tower-layer", + "tower-service", +] + +[[package]] +name = "axum-core" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "759fa577a247914fd3f7f76d62972792636412fbfd634cd452f6a385a74d2d2c" +dependencies = [ + "async-trait", + "bytes", + "futures-util", + "http", + "http-body", + "mime", + "rustversion", + "tower-layer", + "tower-service", +] + +[[package]] +name = "backend_rs" +version = "0.1.0" +dependencies = [ + "axum", + "neo4rs", + "serde", + "serde_json", + "tokio", + "uuid", +] + +[[package]] +name = "backoff" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b62ddb9cb1ec0a098ad4bbf9344d0713fa193ae1a80af55febcff2627b6a00c1" +dependencies = [ + "futures-core", + "getrandom 0.2.16", + "instant", + "pin-project-lite", + "rand", + "tokio", +] + +[[package]] +name = "backtrace" +version = "0.3.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002" +dependencies = [ + "addr2line", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", + "windows-targets", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" + +[[package]] +name = "bumpalo" +version = "3.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" + +[[package]] +name = "bytes" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" +dependencies = [ + "serde", +] + +[[package]] +name = "cc" +version = "1.2.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "deec109607ca693028562ed836a5f1c4b8bd77755c4e132fc5ce11b0b6211ae7" +dependencies = [ + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" + +[[package]] +name = "chrono" +version = "0.4.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d" +dependencies = [ + "num-traits", + "serde", +] + +[[package]] +name = "chrono-tz" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d59ae0466b83e838b81a54256c39d5d7c20b9d7daa10510a242d9b75abd5936e" +dependencies = [ + "chrono", + "chrono-tz-build", + "phf", +] + +[[package]] +name = "chrono-tz-build" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "433e39f13c9a060046954e0592a8d0a4bcb1040125cbf91cb8ee58964cfb350f" +dependencies = [ + "parse-zoneinfo", + "phf", + "phf_codegen", +] + +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "deadpool" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "421fe0f90f2ab22016f32a9881be5134fdd71c65298917084b0c7477cbc3856e" +dependencies = [ + "async-trait", + "deadpool-runtime", + "num_cpus", + "retain_mut", + "tokio", +] + +[[package]] +name = "deadpool-runtime" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "092966b41edc516079bdf31ec78a2e0588d1d0c08f78b91d8307215928642b2b" + +[[package]] +name = "delegate" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ee5df75c70b95bd3aacc8e2fd098797692fb1d54121019c4de481e42f04c8a1" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "futures" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" + +[[package]] +name = "futures-executor" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" + +[[package]] +name = "futures-macro" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "futures-sink" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" + +[[package]] +name = "futures-task" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" + +[[package]] +name = "futures-util" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "getrandom" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.11.1+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasi 0.14.2+wasi-0.2.4", +] + +[[package]] +name = "gimli" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" + +[[package]] +name = "hermit-abi" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" + +[[package]] +name = "http" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" +dependencies = [ + "bytes", + "http", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "hyper" +version = "0.14.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41dfc780fdec9373c01bae43289ea34c972e40ee3c9f6b3c8801a35f35586ce7" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "icu_collections" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47" +dependencies = [ + "displaydoc", + "potential_utf", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locale_core" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_normalizer" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3" + +[[package]] +name = "icu_properties" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "016c619c1eeb94efb86809b015c58f479963de65bdb6253345c1a1276f22e32b" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "potential_utf", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632" + +[[package]] +name = "icu_provider" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af" +dependencies = [ + "displaydoc", + "icu_locale_core", + "stable_deref_trait", + "tinystr", + "writeable", + "yoke", + "zerofrom", + "zerotrie", + "zerovec", +] + +[[package]] +name = "idna" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + +[[package]] +name = "instant" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "io-uring" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b86e202f00093dcba4275d4636b93ef9dd75d025ae560d2521b45ea28ab49013" +dependencies = [ + "bitflags 2.9.1", + "cfg-if", + "libc", +] + +[[package]] +name = "itoa" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" + +[[package]] +name = "js-sys" +version = "0.3.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "libc" +version = "0.2.174" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" + +[[package]] +name = "litemap" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" + +[[package]] +name = "lock_api" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" + +[[package]] +name = "matchit" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" + +[[package]] +name = "memchr" +version = "2.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "miniz_oxide" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" +dependencies = [ + "adler2", +] + +[[package]] +name = "mio" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" +dependencies = [ + "libc", + "wasi 0.11.1+wasi-snapshot-preview1", + "windows-sys 0.59.0", +] + +[[package]] +name = "neo4rs" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43dd99fe7dbc68f754759874d83ec2ca43a61ab7d51c10353d024094805382be" +dependencies = [ + "async-trait", + "backoff", + "bytes", + "chrono", + "chrono-tz", + "deadpool", + "delegate", + "futures", + "log", + "neo4rs-macros", + "paste", + "pin-project-lite", + "rustls-native-certs", + "rustls-pemfile", + "serde", + "thiserror", + "tokio", + "tokio-rustls", + "url", + "webpki-roots 0.26.11", +] + +[[package]] +name = "neo4rs-macros" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53a0d57c55d2d1dc62a2b1d16a0a1079eb78d67c36bdf468d582ab4482ec7002" +dependencies = [ + "quote", + "syn 2.0.104", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91df4bbde75afed763b708b7eee1e8e7651e02d97f6d5dd763e89367e957b23b" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "object" +version = "0.36.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "openssl-probe" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" + +[[package]] +name = "parking_lot" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70d58bf43669b5795d1576d0641cfb6fbb2057bf629506267a92807158584a13" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets", +] + +[[package]] +name = "parse-zoneinfo" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f2a05b18d44e2957b88f96ba460715e295bc1d7510468a2f3d3b44535d26c24" +dependencies = [ + "regex", +] + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "phf" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078" +dependencies = [ + "phf_shared", +] + +[[package]] +name = "phf_codegen" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aef8048c789fa5e851558d709946d6d79a8ff88c0440c587967f8e94bfb1216a" +dependencies = [ + "phf_generator", + "phf_shared", +] + +[[package]] +name = "phf_generator" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" +dependencies = [ + "phf_shared", + "rand", +] + +[[package]] +name = "phf_shared" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5" +dependencies = [ + "siphasher", +] + +[[package]] +name = "pin-project" +version = "1.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677f1add503faace112b9f1373e43e9e054bfdd22ff1a63c1bc485eaec6a6a8a" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "potential_utf" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5a7c30837279ca13e7c867e9e40053bc68740f988cb07f7ca6df43cc734b585" +dependencies = [ + "zerovec", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "proc-macro2" +version = "1.0.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a2af567ac9e1e7d2d11097ca14a1e463a5c06a2ee84c23c009b489b00afc1fc" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.16", +] + +[[package]] +name = "redox_syscall" +version = "0.5.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e8af0dde094006011e6a740d4879319439489813bd0bcdc7d821beaeeff48ec" +dependencies = [ + "bitflags 2.9.1", +] + +[[package]] +name = "regex" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" + +[[package]] +name = "retain_mut" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4389f1d5789befaf6029ebd9f7dac4af7f7e3d61b69d4f30e2ac02b57e7712b0" + +[[package]] +name = "ring" +version = "0.17.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" +dependencies = [ + "cc", + "cfg-if", + "getrandom 0.2.16", + "libc", + "untrusted", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "989e6739f80c4ad5b13e0fd7fe89531180375b18520cc8c82080e4dc4035b84f" + +[[package]] +name = "rustls" +version = "0.23.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2491382039b29b9b11ff08b76ff6c97cf287671dbb74f0be44bda389fffe9bd1" +dependencies = [ + "once_cell", + "ring", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-native-certs" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5bfb394eeed242e909609f56089eecfe5fda225042e8b171791b9c95f5931e5" +dependencies = [ + "openssl-probe", + "rustls-pemfile", + "rustls-pki-types", + "schannel", + "security-framework", +] + +[[package]] +name = "rustls-pemfile" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "rustls-pki-types" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "229a4a4c221013e7e1f1a043678c5cc39fe5171437c88fb47151a21e6f5b5c79" +dependencies = [ + "zeroize", +] + +[[package]] +name = "rustls-webpki" +version = "0.103.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a17884ae0c1b773f1ccd2bd4a8c72f16da897310a98b0e84bf349ad5ead92fc" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", +] + +[[package]] +name = "rustversion" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d" + +[[package]] +name = "ryu" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" + +[[package]] +name = "schannel" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "security-framework" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" +dependencies = [ + "bitflags 2.9.1", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49db231d56a190491cb4aeda9527f1ad45345af50b0851622a7adb8c03b01c32" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "serde" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "serde_json" +version = "1.0.141" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30b9eff21ebe718216c6ec64e1d9ac57087aad11efc64e32002bce4a0d4c03d3" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "serde_path_to_error" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59fab13f937fa393d08645bf3a84bdfe86e296747b506ada67bb15f10f218b2a" +dependencies = [ + "itoa", + "serde", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "signal-hook-registry" +version = "1.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9203b8055f63a2a00e2f593bb0510367fe707d7ff1e5c872de2f537b339e5410" +dependencies = [ + "libc", +] + +[[package]] +name = "siphasher" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" + +[[package]] +name = "slab" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04dc19736151f35336d325007ac991178d504a119863a2fcb3758cdb5e52c50d" + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + +[[package]] +name = "socket2" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "sync_wrapper" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" + +[[package]] +name = "synstructure" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "tinystr" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b" +dependencies = [ + "displaydoc", + "zerovec", +] + +[[package]] +name = "tokio" +version = "1.46.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cc3a2344dafbe23a245241fe8b09735b521110d30fcefbbd5feb1797ca35d17" +dependencies = [ + "backtrace", + "bytes", + "io-uring", + "libc", + "mio", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "slab", + "socket2", + "tokio-macros", + "windows-sys 0.52.0", +] + +[[package]] +name = "tokio-macros" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "tokio-rustls" +version = "0.26.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e727b36a1a0e8b74c376ac2211e40c2c8af09fb4013c60d910495810f008e9b" +dependencies = [ + "rustls", + "tokio", +] + +[[package]] +name = "tower" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" +dependencies = [ + "futures-core", + "futures-util", + "pin-project", + "pin-project-lite", + "tokio", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-layer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" + +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + +[[package]] +name = "tracing" +version = "0.1.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" +dependencies = [ + "log", + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" +dependencies = [ + "once_cell", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] +name = "unicode-ident" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "url" +version = "2.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + +[[package]] +name = "uuid" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3cf4199d1e5d15ddd86a694e4d0dffa9c323ce759fea589f00fef9d81cc1931d" +dependencies = [ + "getrandom 0.3.3", + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasi" +version = "0.14.2+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" +dependencies = [ + "wit-bindgen-rt", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" +dependencies = [ + "bumpalo", + "log", + "proc-macro2", + "quote", + "syn 2.0.104", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "webpki-roots" +version = "0.26.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9" +dependencies = [ + "webpki-roots 1.0.2", +] + +[[package]] +name = "webpki-roots" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e8983c3ab33d6fb807cfcdad2491c4ea8cbc8ed839181c7dfd9c67c83e261b2" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "wit-bindgen-rt" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" +dependencies = [ + "bitflags 2.9.1", +] + +[[package]] +name = "writeable" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" + +[[package]] +name = "yoke" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", + "synstructure", +] + +[[package]] +name = "zerocopy" +version = "0.8.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1039dd0d3c310cf05de012d8a39ff557cb0d23087fd44cad61df08fc31907a2f" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "zerofrom" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", + "synstructure", +] + +[[package]] +name = "zeroize" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" + +[[package]] +name = "zerotrie" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + +[[package]] +name = "zerovec" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a05eb080e015ba39cc9e23bbe5e7fb04d5fb040350f99f34e338d5fdd294428" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] diff --git a/meshwork/backend_rs/Cargo.toml b/meshwork/backend_rs/Cargo.toml new file mode 100644 index 0000000..42097da --- /dev/null +++ b/meshwork/backend_rs/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "backend_rs" +version = "0.1.0" +edition = "2021" + +[dependencies] +axum = "0.6" +tokio = { version = "1.0", features = ["full"] } +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" +neo4rs = "0.8.0" + +[dependencies.uuid] +version = "1.17.0" +# Lets you generate random UUIDs +features = [ + "v4", +] diff --git a/meshwork/backend_rs/Dockerfile b/meshwork/backend_rs/Dockerfile new file mode 100644 index 0000000..11dc701 --- /dev/null +++ b/meshwork/backend_rs/Dockerfile @@ -0,0 +1,7 @@ +FROM rust + +COPY ./ ./ + +RUN cargo build --release + +CMD ["./target/release/backend_rs"] diff --git a/meshwork/backend_rs/src/main.rs b/meshwork/backend_rs/src/main.rs new file mode 100644 index 0000000..7847028 --- /dev/null +++ b/meshwork/backend_rs/src/main.rs @@ -0,0 +1,187 @@ +use axum::{ + extract::State, + routing::{delete, get, post, put}, + Json, Router, +}; +use neo4rs::*; +use serde::{Deserialize, Serialize}; +use std::net::SocketAddr; +use std::sync::Arc; +use uuid::Uuid; +mod schemas; + +// Application state +#[derive(Clone)] +struct AppState { + graph: Arc, +} + +#[tokio::main] +async fn main() { + // Build the router + let uri = "neo4j:7687"; + let user = "neo4j"; + let pass = "your_password"; + + println!("Attempting to connect to Neo4j at {}", uri); + let graph = match Graph::new(uri, user, pass).await { + Ok(graph) => { + println!("Successfully connected to Neo4j"); + graph + } + Err(e) => { + eprintln!("Failed to connect to Neo4j: {}", e); + eprintln!("Make sure Neo4j service is running and accessible"); + std::process::exit(1); + } + }; + + let state = AppState { + graph: Arc::new(graph), + }; + + let app = Router::new() + .route("/", get(root)) + .route("/health", get(health)) + .route("/v0/create_graph", post(create_graph)) + .route("/v0/:graph_id/task/add/", post(add_task)) + .route("/v0/:graph_id/all_tasks", get(get_all_tasks)) + .route("/v0/:graph_id/task/:task_id", get(get_task)) + .route("/v0/:graph_id/task/:task_id", delete(delete_task)) + .route("/v0/:graph_id/task/:task_id", put(edit_task)) + .route("/v0/:graph_id/connect_nodes/", post(connect_nodes)) + .route("/v0/:graph_id/disconnect_nodes/", post(disconnect_nodes)) + .with_state(state); + // Specify the address to listen on + let addr = SocketAddr::from(([0, 0, 0, 0], 8000)); + println!("Server running at http://{}", addr); + // Start the server + axum::Server::bind(&addr) + .serve(app.into_make_service()) + .await + .unwrap(); +} +// Root handler +async fn root() -> &'static str { + "Welcome to the Rust Web Server!" +} + +// Health check +#[derive(Serialize)] +struct HealthResponse { + status: String, + service: String, +} + +async fn health() -> Json { + Json(HealthResponse { + status: "healthy".to_string(), + service: "meshwork-backend".to_string(), + }) +} + +#[derive(Serialize)] +struct CreateGraphResponse { + #[serde(rename = "ID")] + id: String, +} + +#[derive(Serialize)] +struct MessageResponse { + message: String, +} + +#[derive(Deserialize)] +struct ConnectNodesRequest { + node_dependency: String, + node_dependee: String, +} + +async fn create_graph(State(state): State) -> Json { + let id = Uuid::new_v4().to_string(); + let mut txn = state.graph.start_txn().await.unwrap(); + let q = "CREATE (n:Graph {id: $id}) RETURN n"; + txn.run(query(q).param("id", id.clone())).await.unwrap(); + txn.commit().await.unwrap(); + Json(CreateGraphResponse { id }) +} + +async fn add_task( + State(state): State, + axum::extract::Path(graph_id): axum::extract::Path, + Json(task): Json, +) -> Json { + Json(MessageResponse { + message: format!("Task {} added", task.id), + }) +} + +async fn get_all_tasks( + State(state): State, + axum::extract::Path(graph_id): axum::extract::Path, +) -> Json> { + // Return empty vector for now - implementation needed + Json(vec![]) +} + +async fn get_task( + State(state): State, + axum::extract::Path((graph_id, task_id)): axum::extract::Path<(String, String)>, +) -> Json { + // Return dummy task for now - implementation needed + Json(schemas::Task { + id: task_id, + name: "Dummy Task".to_string(), + description: "Placeholder".to_string(), + depends_on: vec![], + users: vec![], + tags: vec![], + status: schemas::Status::TODO, + graph_id, + }) +} + +async fn delete_task( + State(state): State, + axum::extract::Path((graph_id, task_id)): axum::extract::Path<(String, String)>, +) -> Json { + Json(MessageResponse { + message: format!("Task {} deleted", task_id), + }) +} + +async fn edit_task( + State(state): State, + axum::extract::Path((graph_id, task_id)): axum::extract::Path<(String, String)>, + Json(new_task): Json, +) -> Json { + Json(MessageResponse { + message: format!("Task {} edited", task_id), + }) +} + +async fn connect_nodes( + State(state): State, + axum::extract::Path(graph_id): axum::extract::Path, + Json(request): Json, +) -> Json { + Json(MessageResponse { + message: format!( + "Nodes {} and {} connected", + request.node_dependency, request.node_dependee + ), + }) +} + +async fn disconnect_nodes( + State(state): State, + axum::extract::Path(graph_id): axum::extract::Path, + Json(request): Json, +) -> Json { + Json(MessageResponse { + message: format!( + "Nodes {} and {} disconnected", + request.node_dependency, request.node_dependee + ), + }) +} diff --git a/meshwork/backend_rs/src/schemas.rs b/meshwork/backend_rs/src/schemas.rs new file mode 100644 index 0000000..2ebcee4 --- /dev/null +++ b/meshwork/backend_rs/src/schemas.rs @@ -0,0 +1,22 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Serialize, Deserialize)] +pub enum Status { + TODO, + IN_PROGRESS, + DONE, + REVIEW, + BLOCKED +} + +#[derive(Serialize, Deserialize)] +pub struct Task { + pub id: String, + pub name: String, + pub description: String, + pub depends_on: Vec, + pub users: Vec, + pub tags: Vec, + pub status: Status, + pub graph_id: String, +} From 3697dc3b943cdd60766a208b379589852924acd2 Mon Sep 17 00:00:00 2001 From: NotBioWaste905 Date: Tue, 22 Jul 2025 12:48:47 +0300 Subject: [PATCH 3/6] Implement creage_graph, add_task and get_all_tasks, add cargo check workflow --- .github/workflows/backend_build_check.yml | 43 ++++ .github/workflows/build_test_react.yml | 38 --- meshwork/backend_rs/Cargo.lock | 169 +++++++++++++- meshwork/backend_rs/Cargo.toml | 8 +- meshwork/backend_rs/src/main.rs | 273 ++++++++++++++++++---- meshwork/backend_rs/src/schemas.rs | 8 +- 6 files changed, 452 insertions(+), 87 deletions(-) create mode 100644 .github/workflows/backend_build_check.yml delete mode 100644 .github/workflows/build_test_react.yml diff --git a/.github/workflows/backend_build_check.yml b/.github/workflows/backend_build_check.yml new file mode 100644 index 0000000..153b893 --- /dev/null +++ b/.github/workflows/backend_build_check.yml @@ -0,0 +1,43 @@ +name: Check Rust Backend + +on: + push: + branches: [main] + pull_request: + branches: [main] + +jobs: + check: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + + - name: Install Rust + uses: actions-rs/toolchain@v1 + with: + toolchain: stable + override: true + components: rustfmt, clippy + + - name: Cache cargo registry + uses: actions/cache@v3 + with: + path: ~/.cargo/registry + key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }} + + - name: Cache cargo index + uses: actions/cache@v3 + with: + path: ~/.cargo/git + key: ${{ runner.os }}-cargo-index-${{ hashFiles('**/Cargo.lock') }} + + - name: Cache cargo build + uses: actions/cache@v3 + with: + path: Meshwork/meshwork/backend_rs/target + key: ${{ runner.os }}-cargo-build-target-${{ hashFiles('**/Cargo.lock') }} + + - name: Run cargo check + working-directory: Meshwork/meshwork/backend_rs + run: cargo check diff --git a/.github/workflows/build_test_react.yml b/.github/workflows/build_test_react.yml deleted file mode 100644 index d145a4a..0000000 --- a/.github/workflows/build_test_react.yml +++ /dev/null @@ -1,38 +0,0 @@ -name: Build and Test React Application - -# Controls when the action will run. -on: - # Triggers the workflow on push or pull request events but only for the main branch - push: - branches: [main] - pull_request: - branches: [main] - -defaults: - run: - shell: bash - working-directory: ./meshwork/frontend/meshwork-ui - -# A workflow run is made up of one or more jobs that can run sequentially or in parallel -jobs: - build_test: - # The type of runner that the job will run on - runs-on: ubuntu-latest - - strategy: - matrix: - node-version: [18.x, 20.x] # LTS and current versions - - # Steps represent a sequence of tasks that will be executed as part of the job - steps: - # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it - - uses: actions/checkout@v2 - - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v2 - with: - node-version: ${{ matrix.node-version }} - - name: npm ci, build and test - run: | - npm ci - npm run build --if-present - npm test diff --git a/meshwork/backend_rs/Cargo.lock b/meshwork/backend_rs/Cargo.lock index e5163ca..45c21c3 100644 --- a/meshwork/backend_rs/Cargo.lock +++ b/meshwork/backend_rs/Cargo.lock @@ -101,6 +101,10 @@ dependencies = [ "serde", "serde_json", "tokio", + "tower", + "tower-http", + "tracing", + "tracing-subscriber", "uuid", ] @@ -425,6 +429,12 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "http-range-header" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "add0ab9360ddbd88cfeb3bd9574a1d85cfdfa14db10b3e21d3700dbc4328758f" + [[package]] name = "httparse" version = "1.10.1" @@ -603,6 +613,12 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + [[package]] name = "libc" version = "0.2.174" @@ -631,6 +647,15 @@ version = "0.4.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" +[[package]] +name = "matchers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +dependencies = [ + "regex-automata 0.1.10", +] + [[package]] name = "matchit" version = "0.7.3" @@ -707,6 +732,16 @@ dependencies = [ "syn 2.0.104", ] +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi", +] + [[package]] name = "num-traits" version = "0.2.19" @@ -747,6 +782,12 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + [[package]] name = "parking_lot" version = "0.12.4" @@ -950,8 +991,17 @@ checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" dependencies = [ "aho-corasick", "memchr", - "regex-automata", - "regex-syntax", + "regex-automata 0.4.9", + "regex-syntax 0.8.5", +] + +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +dependencies = [ + "regex-syntax 0.6.29", ] [[package]] @@ -962,9 +1012,15 @@ checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" dependencies = [ "aho-corasick", "memchr", - "regex-syntax", + "regex-syntax 0.8.5", ] +[[package]] +name = "regex-syntax" +version = "0.6.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + [[package]] name = "regex-syntax" version = "0.8.5" @@ -1157,6 +1213,15 @@ dependencies = [ "serde", ] +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + [[package]] name = "shlex" version = "1.3.0" @@ -1271,6 +1336,15 @@ dependencies = [ "syn 2.0.104", ] +[[package]] +name = "thread_local" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" +dependencies = [ + "cfg-if", +] + [[package]] name = "tinystr" version = "0.8.1" @@ -1338,6 +1412,25 @@ dependencies = [ "tracing", ] +[[package]] +name = "tower-http" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61c5bb1d698276a2443e5ecfabc1008bf15a36c12e6a7176e7bf089ea9131140" +dependencies = [ + "bitflags 2.9.1", + "bytes", + "futures-core", + "futures-util", + "http", + "http-body", + "http-range-header", + "pin-project-lite", + "tower-layer", + "tower-service", + "tracing", +] + [[package]] name = "tower-layer" version = "0.3.3" @@ -1358,9 +1451,21 @@ checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" dependencies = [ "log", "pin-project-lite", + "tracing-attributes", "tracing-core", ] +[[package]] +name = "tracing-attributes" +version = "0.1.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + [[package]] name = "tracing-core" version = "0.1.34" @@ -1368,6 +1473,36 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" dependencies = [ "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" +dependencies = [ + "matchers", + "nu-ansi-term", + "once_cell", + "regex", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", ] [[package]] @@ -1416,6 +1551,12 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "valuable" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" + [[package]] name = "want" version = "0.3.1" @@ -1516,6 +1657,28 @@ dependencies = [ "rustls-pki-types", ] +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + [[package]] name = "windows-sys" version = "0.52.0" diff --git a/meshwork/backend_rs/Cargo.toml b/meshwork/backend_rs/Cargo.toml index 42097da..a65a981 100644 --- a/meshwork/backend_rs/Cargo.toml +++ b/meshwork/backend_rs/Cargo.toml @@ -9,10 +9,12 @@ tokio = { version = "1.0", features = ["full"] } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" neo4rs = "0.8.0" +tracing = "0.1" +tracing-subscriber = { version = "0.3", features = ["env-filter"] } +tower = "0.4" +tower-http = { version = "0.4", features = ["trace"] } [dependencies.uuid] version = "1.17.0" # Lets you generate random UUIDs -features = [ - "v4", -] +features = ["v4"] diff --git a/meshwork/backend_rs/src/main.rs b/meshwork/backend_rs/src/main.rs index 7847028..0eb45af 100644 --- a/meshwork/backend_rs/src/main.rs +++ b/meshwork/backend_rs/src/main.rs @@ -1,5 +1,7 @@ use axum::{ extract::State, + http::StatusCode, + response::{IntoResponse, Response}, routing::{delete, get, post, put}, Json, Router, }; @@ -7,9 +9,50 @@ use neo4rs::*; use serde::{Deserialize, Serialize}; use std::net::SocketAddr; use std::sync::Arc; +use tower_http::trace::TraceLayer; +use tracing::{error, info, warn}; use uuid::Uuid; mod schemas; +// Custom error types +#[derive(Debug)] +enum AppError { + Database(neo4rs::Error), + NotFound(String), + BadRequest(String), + Internal(String), +} + +impl IntoResponse for AppError { + fn into_response(self) -> Response { + let (status, message) = match self { + AppError::Database(e) => { + error!("Database error: {}", e); + ( + StatusCode::INTERNAL_SERVER_ERROR, + "Database error occurred".to_string(), + ) + } + AppError::NotFound(msg) => { + warn!("Resource not found: {}", msg); + (StatusCode::NOT_FOUND, msg) + } + AppError::BadRequest(msg) => { + warn!("Bad request: {}", msg); + (StatusCode::BAD_REQUEST, msg) + } + AppError::Internal(msg) => { + error!("Internal error: {}", msg); + (StatusCode::INTERNAL_SERVER_ERROR, msg) + } + }; + + (status, Json(MessageResponse { message })).into_response() + } +} + +type Result = std::result::Result; + // Application state #[derive(Clone)] struct AppState { @@ -18,20 +61,28 @@ struct AppState { #[tokio::main] async fn main() { + // Initialize tracing + tracing_subscriber::fmt() + .with_target(false) + .compact() + .init(); + + info!("Starting Meshwork Backend Server"); + // Build the router let uri = "neo4j:7687"; let user = "neo4j"; let pass = "your_password"; - println!("Attempting to connect to Neo4j at {}", uri); + info!("Attempting to connect to Neo4j at {}", uri); let graph = match Graph::new(uri, user, pass).await { Ok(graph) => { - println!("Successfully connected to Neo4j"); + info!("Successfully connected to Neo4j"); graph } Err(e) => { - eprintln!("Failed to connect to Neo4j: {}", e); - eprintln!("Make sure Neo4j service is running and accessible"); + error!("Failed to connect to Neo4j: {}", e); + error!("Make sure Neo4j service is running and accessible"); std::process::exit(1); } }; @@ -51,10 +102,11 @@ async fn main() { .route("/v0/:graph_id/task/:task_id", put(edit_task)) .route("/v0/:graph_id/connect_nodes/", post(connect_nodes)) .route("/v0/:graph_id/disconnect_nodes/", post(disconnect_nodes)) + .layer(TraceLayer::new_for_http()) .with_state(state); // Specify the address to listen on let addr = SocketAddr::from(([0, 0, 0, 0], 8000)); - println!("Server running at http://{}", addr); + info!("Server running at http://{}", addr); // Start the server axum::Server::bind(&addr) .serve(app.into_make_service()) @@ -97,39 +149,160 @@ struct ConnectNodesRequest { node_dependee: String, } -async fn create_graph(State(state): State) -> Json { +async fn create_graph(State(state): State) -> Result> { let id = Uuid::new_v4().to_string(); - let mut txn = state.graph.start_txn().await.unwrap(); + info!("Creating new graph with ID: {}", id); + + let mut txn = state.graph.start_txn().await.map_err(AppError::Database)?; + let q = "CREATE (n:Graph {id: $id}) RETURN n"; - txn.run(query(q).param("id", id.clone())).await.unwrap(); - txn.commit().await.unwrap(); - Json(CreateGraphResponse { id }) + txn.run(query(q).param("id", id.clone())) + .await + .map_err(AppError::Database)?; + + txn.commit().await.map_err(AppError::Database)?; + + info!("Graph created successfully with ID: {}", id); + Ok(Json(CreateGraphResponse { id })) } async fn add_task( State(state): State, axum::extract::Path(graph_id): axum::extract::Path, Json(task): Json, -) -> Json { - Json(MessageResponse { - message: format!("Task {} added", task.id), - }) +) -> Result> { + info!("Adding task '{}' to graph '{}'", task.id, graph_id); + + // Input validation + if task.id.trim().is_empty() { + return Err(AppError::BadRequest("Task ID cannot be empty".to_string())); + } + if task.name.trim().is_empty() { + return Err(AppError::BadRequest( + "Task name cannot be empty".to_string(), + )); + } + + let mut txn = state.graph.start_txn().await.map_err(AppError::Database)?; + + // Create the task + let q = "CREATE (t:Task {id: $id, name: $name, description: $description, status: $status}) RETURN t"; + txn.run( + query(q) + .param("id", task.id.clone()) + .param("name", task.name.clone()) + .param("description", task.description.clone()) + .param("status", format!("{:?}", task.status)), + ) + .await + .map_err(AppError::Database)?; + + // Link task to graph + let q = "MATCH (t:Task {id: $id}), (g:Graph {id: $graph_id}) CREATE (t)-[rel:PART_OF]->(g) RETURN t"; + txn.run( + query(q) + .param("id", task.id.clone()) + .param("graph_id", graph_id.clone()), + ) + .await + .map_err(AppError::Database)?; + + // Create dependencies + for dependency in &task.depends_on { + info!("Adding dependency: {} -> {}", task.id, dependency); + let q = "MATCH (t:Task {id: $id}), (dep_task:Task {id: $dependency_id}) CREATE (t)-[rel:DEPENDS_ON]->(dep_task) RETURN t"; + txn.run( + query(q) + .param("id", task.id.clone()) + .param("dependency_id", dependency.clone()), + ) + .await + .map_err(AppError::Database)?; + } + + // Assign users + for user in &task.users { + info!("Assigning user '{}' to task '{}'", user, task.id); + let q = "MERGE (user:User {id: $user_id}) WITH user MATCH (t:Task {id: $id}) CREATE (t)-[rel:ASSIGNED_TO]->(user) RETURN t"; + txn.run( + query(q) + .param("id", task.id.clone()) + .param("user_id", user.clone()), + ) + .await + .map_err(AppError::Database)?; + } + + txn.commit().await.map_err(AppError::Database)?; + + info!( + "Task '{}' added successfully to graph '{}'", + task.id, graph_id + ); + Ok(Json(MessageResponse { + message: format!("Task {} added successfully", task.id), + })) } async fn get_all_tasks( State(state): State, axum::extract::Path(graph_id): axum::extract::Path, -) -> Json> { - // Return empty vector for now - implementation needed - Json(vec![]) +) -> Result>> { + info!("Fetching all tasks for graph '{}'", graph_id); + + if graph_id.trim().is_empty() { + return Err(AppError::BadRequest("Graph ID cannot be empty".to_string())); + } + + let q = r#" + MATCH (t:Task)-[:PART_OF]->(g:Graph {id: $id}) + OPTIONAL MATCH (t)-[:DEPENDS_ON]->(dep:Task) + OPTIONAL MATCH (t)-[:ASSIGNED_TO]->(u:User) + RETURN t, + collect(DISTINCT dep.id) as dependencies, + collect(DISTINCT u.id) as users + "#; + + let mut result = state + .graph + .execute(query(q).param("id", graph_id.clone())) + .await + .map_err(AppError::Database)?; + + let mut tasks = Vec::new(); + + while let Ok(Some(record)) = result.next().await { + let node: neo4rs::Node = record.get("t").unwrap(); + let dependencies: Vec = record.get("dependencies").unwrap_or_default(); + let users: Vec = record.get("users").unwrap_or_default(); + + let task = schemas::Task { + id: node.get("id").unwrap(), + name: node.get("name").unwrap(), + description: node.get("description").unwrap(), + status: serde_json::from_str(&format!("\"{}\"", node.get::("status").unwrap())) + .unwrap(), + depends_on: dependencies, + users, + tags: vec![], // You'll need to handle tags similarly if you use them + graph_id: graph_id.clone(), + }; + + tasks.push(task); + } + + Ok(Json(tasks)) } async fn get_task( - State(state): State, + State(_state): State, axum::extract::Path((graph_id, task_id)): axum::extract::Path<(String, String)>, -) -> Json { - // Return dummy task for now - implementation needed - Json(schemas::Task { +) -> Result> { + info!("Fetching task '{}' from graph '{}'", task_id, graph_id); + + // TODO: Implement actual database query + warn!("get_task not yet implemented - returning dummy task"); + Ok(Json(schemas::Task { id: task_id, name: "Dummy Task".to_string(), description: "Placeholder".to_string(), @@ -138,50 +311,72 @@ async fn get_task( tags: vec![], status: schemas::Status::TODO, graph_id, - }) + })) } async fn delete_task( - State(state): State, + State(_state): State, axum::extract::Path((graph_id, task_id)): axum::extract::Path<(String, String)>, -) -> Json { - Json(MessageResponse { +) -> Result> { + info!("Deleting task '{}' from graph '{}'", task_id, graph_id); + + // TODO: Implement actual database deletion + warn!("delete_task not yet implemented"); + Ok(Json(MessageResponse { message: format!("Task {} deleted", task_id), - }) + })) } async fn edit_task( - State(state): State, + State(_state): State, axum::extract::Path((graph_id, task_id)): axum::extract::Path<(String, String)>, - Json(new_task): Json, -) -> Json { - Json(MessageResponse { + Json(_new_task): Json, +) -> Result> { + info!("Editing task '{}' in graph '{}'", task_id, graph_id); + + // TODO: Implement actual database update + warn!("edit_task not yet implemented"); + Ok(Json(MessageResponse { message: format!("Task {} edited", task_id), - }) + })) } async fn connect_nodes( - State(state): State, + State(_state): State, axum::extract::Path(graph_id): axum::extract::Path, Json(request): Json, -) -> Json { - Json(MessageResponse { +) -> Result> { + info!( + "Connecting nodes '{}' -> '{}' in graph '{}'", + request.node_dependency, request.node_dependee, graph_id + ); + + // TODO: Implement actual database connection + warn!("connect_nodes not yet implemented"); + Ok(Json(MessageResponse { message: format!( "Nodes {} and {} connected", request.node_dependency, request.node_dependee ), - }) + })) } async fn disconnect_nodes( - State(state): State, + State(_state): State, axum::extract::Path(graph_id): axum::extract::Path, Json(request): Json, -) -> Json { - Json(MessageResponse { +) -> Result> { + info!( + "Disconnecting nodes '{}' -> '{}' in graph '{}'", + request.node_dependency, request.node_dependee, graph_id + ); + + // TODO: Implement actual database disconnection + warn!("disconnect_nodes not yet implemented"); + Ok(Json(MessageResponse { message: format!( "Nodes {} and {} disconnected", request.node_dependency, request.node_dependee ), - }) + })) } diff --git a/meshwork/backend_rs/src/schemas.rs b/meshwork/backend_rs/src/schemas.rs index 2ebcee4..ba6c6ec 100644 --- a/meshwork/backend_rs/src/schemas.rs +++ b/meshwork/backend_rs/src/schemas.rs @@ -1,15 +1,15 @@ use serde::{Deserialize, Serialize}; -#[derive(Serialize, Deserialize)] +#[derive(Serialize, Deserialize, Debug)] pub enum Status { TODO, - IN_PROGRESS, + InProgress, DONE, REVIEW, - BLOCKED + BLOCKED, } -#[derive(Serialize, Deserialize)] +#[derive(Serialize, Deserialize, Debug)] pub struct Task { pub id: String, pub name: String, From c2179741480988d1fd618c87ebd2be75d1b63090 Mon Sep 17 00:00:00 2001 From: NotBioWaste905 Date: Tue, 22 Jul 2025 12:51:41 +0300 Subject: [PATCH 4/6] Hopefully fix the workflow --- .github/workflows/backend_build_check.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/backend_build_check.yml b/.github/workflows/backend_build_check.yml index 153b893..cb62bce 100644 --- a/.github/workflows/backend_build_check.yml +++ b/.github/workflows/backend_build_check.yml @@ -32,12 +32,12 @@ jobs: path: ~/.cargo/git key: ${{ runner.os }}-cargo-index-${{ hashFiles('**/Cargo.lock') }} - - name: Cache cargo build - uses: actions/cache@v3 - with: - path: Meshwork/meshwork/backend_rs/target - key: ${{ runner.os }}-cargo-build-target-${{ hashFiles('**/Cargo.lock') }} + # - name: Cache cargo build + # uses: actions/cache@v3 + # with: + # path: Meshwork/meshwork/backend_rs/target + # key: ${{ runner.os }}-cargo-build-target-${{ hashFiles('**/Cargo.lock') }} - - name: Run cargo check + - name: Run cargo build working-directory: Meshwork/meshwork/backend_rs - run: cargo check + run: cargo build From 7a18b4ac12d6ddc16079421dbae0844f43f4e3bd Mon Sep 17 00:00:00 2001 From: NotBioWaste905 Date: Tue, 22 Jul 2025 12:53:09 +0300 Subject: [PATCH 5/6] fix/resolving path issue --- .github/workflows/backend_build_check.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/backend_build_check.yml b/.github/workflows/backend_build_check.yml index cb62bce..c906bc2 100644 --- a/.github/workflows/backend_build_check.yml +++ b/.github/workflows/backend_build_check.yml @@ -39,5 +39,5 @@ jobs: # key: ${{ runner.os }}-cargo-build-target-${{ hashFiles('**/Cargo.lock') }} - name: Run cargo build - working-directory: Meshwork/meshwork/backend_rs + working-directory: meshwork/backend_rs run: cargo build From 5c8dcb62499aab8891b4ee0b6415c172d075ad23 Mon Sep 17 00:00:00 2001 From: NotBioWaste905 Date: Tue, 22 Jul 2025 12:54:32 +0300 Subject: [PATCH 6/6] fix/rename the workflow --- .github/workflows/backend_build_check.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/backend_build_check.yml b/.github/workflows/backend_build_check.yml index c906bc2..0d254ae 100644 --- a/.github/workflows/backend_build_check.yml +++ b/.github/workflows/backend_build_check.yml @@ -1,4 +1,4 @@ -name: Check Rust Backend +name: Check Rust Backend Build on: push: @@ -35,7 +35,7 @@ jobs: # - name: Cache cargo build # uses: actions/cache@v3 # with: - # path: Meshwork/meshwork/backend_rs/target + # path: meshwork/backend_rs/target # key: ${{ runner.os }}-cargo-build-target-${{ hashFiles('**/Cargo.lock') }} - name: Run cargo build