docs: document custom type support throughout project

Update all documentation to reflect that custom types work automatically
with the #[task] macro, requiring only Clone + Send + Sync + 'static.

Documentation updates:
- README: Add custom types to features, new Custom Types section
- CHANGELOG: Document custom type improvement in [Unreleased]
- src/lib.rs: Add custom types example to main API docs
- src/task.rs: Update Task trait docs to mention macro-generated extraction
- src/extract.rs: Clarify ExtractInput is now internal-only (for task_fn)
- dagx-macros: Update macro docs to emphasize automatic type handling

New example:
- examples/custom_types.rs: Simple 120-line example showing User type
  flowing through DAG with fan-out pattern (one output → two consumers)
  demonstrating Arc wrapping is automatic and transparent

This makes the custom type capability discoverable and prominent for users,
turning a previous limitation into a headline feature.
This commit is contained in:
swaits committed 2025-10-10 17:22:59 -06:00
1 parent 8ee4092f9a
commit 979232bd67
8 files changed
+366 -7

No files matched your search

+50
View File
@@ -10,6 +10,9 @@
//! - **Compile-time type safety**: Dependencies are validated at compile time through the type
//! system. The public API is fully type-safe with no runtime type errors. Internal execution
//! uses type erasure for heterogeneous task storage, but this is never exposed to users.
//! - **Works with ANY type**: Custom types work automatically with the `#[task]` macro—just
//! implement `Clone + Send + Sync`, no trait implementations needed! The macro generates
//! type-specific extraction logic for seamless integration.
//! - **Runtime-agnostic**: Works with any async runtime (Tokio, async-std, smol, Embassy, etc.)
//! - **Type-state pattern**: The API guides you with compile-time errors if you wire dependencies
//! incorrectly. This is what prevents cycles at compile time.
@@ -433,6 +436,53 @@
//! # };
//! ```
//!
//! ## Custom Types
//!
//! **dagx works with ANY type automatically!** As long as your type implements `Clone + Send + Sync + 'static`,
//! the `#[task]` macro generates the necessary extraction logic:
//!
//! ```no_run
//! use dagx::{task, DagRunner, Task};
//!
//! // Just derive Clone - that's all you need!
//! #[derive(Clone)]
//! struct User {
//! name: String,
//! age: u32,
//! }
//!
//! struct CreateUser;
//! #[task]
//! impl CreateUser {
//! async fn run() -> User {
//! User { name: "Alice".to_string(), age: 30 }
//! }
//! }
//!
//! struct FormatUser;
//! #[task]
//! impl FormatUser {
//! async fn run(user: &User) -> String {
//! format!("{} is {} years old", user.name, user.age)
//! }
//! }
//!
//! # async {
//! let dag = DagRunner::new();
//!
//! let user = dag.add_task(CreateUser);
//! let formatted = dag.add_task(FormatUser).depends_on(&user);
//!
//! dag.run(|fut| { tokio::spawn(fut); }).await.unwrap();
//!
//! assert_eq!(dag.get(formatted).unwrap(), "Alice is 30 years old");
//! # };
//! ```
//!
//! **No trait implementations needed!** Works with nested structs, collections, enums, and any
//! other custom type. See [`examples/custom_types.rs`](https://github.com/swaits/dagx/blob/main/examples/custom_types.rs)
//! for a complete example with complex nested types.
//!
//! # Runtime Agnostic
//!
//! dagx works with any async runtime. The library has been tested with: