chore: clean up in preparation for release

This commit is contained in:
TechnoPorg committed 2026-02-18 22:34:31 +01:00
1 parent c002161216
commit 827041c1b6
28 files changed
+112 -219

No files matched your search

+3 -2
View File
@@ -18,7 +18,8 @@ categories = ["asynchronous", "concurrency", "data-structures"]
exclude = [".claude/", ".jj/", "dagx-spec.md"] exclude = [".claude/", ".jj/", "dagx-spec.md"]
[features] [features]
default = [] default = ["derive"]
derive = ["dep:dagx-macros"]
tracing = ["dep:tracing"] tracing = ["dep:tracing"]
[workspace.lints.rust] [workspace.lints.rust]
@@ -32,7 +33,7 @@ workspace = true
[dependencies] [dependencies]
futures-util = { version = "0.3", default-features = false, features = ["std"] } futures-util = { version = "0.3", default-features = false, features = ["std"] }
dagx-macros = { version = "0.3.1", path = "./dagx-macros" } dagx-macros = { version = "0.3.1", path = "./dagx-macros", optional = true }
tracing = { version = "0.1", optional = true } tracing = { version = "0.1", optional = true }
[dev-dependencies] [dev-dependencies]
+1 -1
View File
@@ -26,7 +26,7 @@ pub fn bench_diamond_pattern(c: &mut Criterion) {
.depends_on(a); .depends_on(a);
let d = dag let d = dag
.add_task(task_fn::<(i32, i32), _, _>(|(x, y): (&i32, &i32)| x + y)) .add_task(task_fn::<(i32, i32), _, _>(|(x, y): (&i32, &i32)| x + y))
.depends_on((&b, &c)); .depends_on((b, c));
let mut output = dag let mut output = dag
.run(|fut| async move { tokio::spawn(fut).await.unwrap() }) .run(|fut| async move { tokio::spawn(fut).await.unwrap() })
+1 -1
View File
@@ -51,7 +51,7 @@ pub fn bench_etl_pipeline(c: &mut Criterion) {
result result
}, },
)) ))
.depends_on((&transform1, &transform2, &transform3)); .depends_on((transform1, transform2, transform3));
// Validate // Validate
let validate = dag let validate = dag
+1 -1
View File
@@ -22,7 +22,7 @@ pub fn bench_diamond(c: &mut Criterion) {
.depends_on(source); .depends_on(source);
dag.add_task(task_fn::<(i32, i32), _, _>(|(l, r): (&i32, &i32)| l + r)) dag.add_task(task_fn::<(i32, i32), _, _>(|(l, r): (&i32, &i32)| l + r))
.depends_on((&left, &right)); .depends_on((left, right));
} }
let _output = dag let _output = dag
+1 -1
View File
@@ -88,7 +88,7 @@ pub fn bench_etl_pipeline(c: &mut Criterion) {
) )
}, },
)) ))
.depends_on((&validate, &scores, &names)); .depends_on((validate, scores, names));
let _output = dag let _output = dag
.run(|fut| async move { tokio::spawn(fut).await.unwrap() }) .run(|fut| async move { tokio::spawn(fut).await.unwrap() })
+1 -1
View File
@@ -42,7 +42,7 @@ pub fn bench_mixed_patterns(c: &mut Criterion) {
format!("Matches: {}, Total bytes: {}, Count: {}", a1, a2, a3) format!("Matches: {}, Total bytes: {}, Count: {}", a1, a2, a3)
}, },
)) ))
.depends_on((&analysis1, &analysis2, &analysis3)); .depends_on((analysis1, analysis2, analysis3));
// Stage 4: Fan-out final report to multiple destinations // Stage 4: Fan-out final report to multiple destinations
for i in 0..3 { for i in 0..3 {
+7 -47
View File
@@ -155,9 +155,10 @@ use syn::{parse_macro_input, FnArg, Ident, ImplItem, ItemImpl, Pat, PatType, Ret
/// impl Task<(i32, i32)> for Add { /// impl Task<(i32, i32)> for Add {
/// type Output = i32; /// type Output = i32;
/// ///
/// async fn run(&mut self, input: Self::Input) -> Self::Output { /// async fn run(&mut self, _input: Self::Input) -> Self::Output {
/// let (a, b) = input; /// let (a, _input) = _input.next();
/// Self::run_impl(&a, &b).await /// let (b, _input) = _input.next();
/// Self::run_impl(a, b).await
/// } /// }
/// } /// }
/// ///
@@ -168,36 +169,6 @@ use syn::{parse_macro_input, FnArg, Ident, ImplItem, ItemImpl, Pat, PatType, Ret
/// } /// }
/// } /// }
/// ``` /// ```
///
/// For stateful tasks (with &mut self):
///
/// ```ignore
/// // Your code:
/// #[task]
/// impl Counter {
/// async fn run(&mut self, inc: &i32) -> i32 {
/// self.count += inc;
/// self.count
/// }
/// }
///
/// // Generated:
/// impl Task<(i32)> for Counter {
/// type Output = i32;
///
/// async fn run(&mut self, input: Self::Input) -> Self::Output {
/// let inc = input;
/// self.run_impl(&inc).await
/// }
/// }
///
/// impl Counter {
/// async fn run_impl(&mut self, inc: &i32) -> i32 {
/// self.count += inc;
/// self.count
/// }
/// }
/// ```
#[proc_macro_attribute] #[proc_macro_attribute]
pub fn task(_attr: TokenStream, item: TokenStream) -> TokenStream { pub fn task(_attr: TokenStream, item: TokenStream) -> TokenStream {
let impl_block = parse_macro_input!(item as ItemImpl); let impl_block = parse_macro_input!(item as ItemImpl);
@@ -273,18 +244,7 @@ pub fn task(_attr: TokenStream, item: TokenStream) -> TokenStream {
// Extract return type // Extract return type
let output_type = match &run_method.sig.output { let output_type = match &run_method.sig.output {
ReturnType::Default => syn::parse_quote!(()), ReturnType::Default => syn::parse_quote!(()),
ReturnType::Type(_, ty) => { ReturnType::Type(_, ty) => ty.clone(),
if let Type::Tuple(_tuple) = &**ty {
return syn::Error::new_spanned(
ty,
"Returning bare tuples from task functions is currently not supported.\n\n\
Please wrap this in a newtype or struct.",
)
.into_compile_error()
.into();
}
ty.clone()
}
}; };
// Build Input type based on parameter count // Build Input type based on parameter count
@@ -299,7 +259,7 @@ pub fn task(_attr: TokenStream, item: TokenStream) -> TokenStream {
let ident = Ident::new(&format!("__dagx_extract_{}", ident), Span::mixed_site()); let ident = Ident::new(&format!("__dagx_extract_{}", ident), Span::mixed_site());
( (
quote! { quote! {
let (#ident, input) = input.next(); let (#ident, _input) = _input.next();
}, },
ident, ident,
) )
@@ -333,7 +293,7 @@ pub fn task(_attr: TokenStream, item: TokenStream) -> TokenStream {
impl ::dagx::Task<#input_type> for #struct_name { impl ::dagx::Task<#input_type> for #struct_name {
type Output = #output_type; type Output = #output_type;
async fn run(mut self, mut input: ::dagx::TaskInput<'_, #input_type>) -> Self::Output { async fn run(mut self, mut _input: ::dagx::TaskInput<'_, #input_type>) -> Self::Output {
#param_destructure #param_destructure
#run_call #run_call
} }
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2025 Stephen Waits <steve@waits.net>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+1 -1
View File
@@ -93,7 +93,7 @@ async fn main() {
.add_task(Add { .add_task(Add {
label: "Sum".to_string(), label: "Sum".to_string(),
}) })
.depends_on((&x, &y)); // Pass handles as a tuple .depends_on((x, y)); // Pass handles as a tuple
// sum is now TaskHandle<i32> // sum is now TaskHandle<i32>
// Step 6: Execute the DAG // Step 6: Execute the DAG
+1 -1
View File
@@ -29,7 +29,7 @@
//! ## Key Concepts //! ## Key Concepts
//! - **Fan-in**: Multiple tasks' outputs are consumed by a single downstream task //! - **Fan-in**: Multiple tasks' outputs are consumed by a single downstream task
//! - **Aggregation**: Combining multiple values into a single result //! - **Aggregation**: Combining multiple values into a single result
//! - **Parameter order**: The order in `.depends_on((&a, &b, &c))` must match //! - **Parameter order**: The order in `.depends_on((a, b, c))` must match
//! the function signature `fn run(a: &T1, b: &T2, c: &T3)` //! the function signature `fn run(a: &T1, b: &T2, c: &T3)`
//! //!
//! ## Use Cases //! ## Use Cases
+9 -9
View File
@@ -52,7 +52,7 @@
//! This proves dagx runs tasks on multiple CPU cores simultaneously. //! This proves dagx runs tasks on multiple CPU cores simultaneously.
//! ``` //! ```
use dagx::{task, DagResult, DagRunner, Task, TaskInput}; use dagx::{task, DagResult, DagRunner};
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
use tokio::time::sleep; use tokio::time::sleep;
@@ -149,15 +149,15 @@ async fn main() -> DagResult<()> {
let start = Instant::now(); let start = Instant::now();
// Run tasks sequentially (not using DAG, just raw execution) // Run tasks sequentially (not using DAG, just raw execution)
let task1 = ComputeSum::new(1, 251, WORK_DELAY); let mut task1 = ComputeSum::new(1, 251, WORK_DELAY);
let task2 = ComputeSum::new(251, 501, WORK_DELAY); let mut task2 = ComputeSum::new(251, 501, WORK_DELAY);
let task3 = ComputeSum::new(501, 751, WORK_DELAY); let mut task3 = ComputeSum::new(501, 751, WORK_DELAY);
let task4 = ComputeSum::new(751, 1001, WORK_DELAY); let mut task4 = ComputeSum::new(751, 1001, WORK_DELAY);
let r1 = task1.run(TaskInput::empty()).await; let r1 = task1.run_impl().await;
let r2 = task2.run(TaskInput::empty()).await; let r2 = task2.run_impl().await;
let r3 = task3.run(TaskInput::empty()).await; let r3 = task3.run_impl().await;
let r4 = task4.run(TaskInput::empty()).await; let r4 = task4.run_impl().await;
let result = r1 + r2 + r3 + r4; let result = r1 + r2 + r3 + r4;
let elapsed = start.elapsed(); let elapsed = start.elapsed();
+4 -6
View File
@@ -203,20 +203,18 @@ async fn main() {
// Stage 3: Statistical analysis (parallel operations on transformed data) // Stage 3: Statistical analysis (parallel operations on transformed data)
let sum = dag let sum = dag
.add_task(ComputeSum) .add_task(ComputeSum)
.depends_on((&transformed_1, &transformed_2, &transformed_3)); .depends_on((transformed_1, transformed_2, transformed_3));
let product = let product =
dag.add_task(ComputeProduct) dag.add_task(ComputeProduct)
.depends_on((&transformed_1, &transformed_2, &transformed_3)); .depends_on((transformed_1, transformed_2, transformed_3));
let max = dag let max = dag
.add_task(FindMax) .add_task(FindMax)
.depends_on((&transformed_1, &transformed_2, &transformed_3)); .depends_on((transformed_1, transformed_2, transformed_3));
// Stage 4: Final report // Stage 4: Final report
let report = dag let report = dag.add_task(GenerateReport).depends_on((sum, product, max));
.add_task(GenerateReport)
.depends_on((&sum, &product, &max));
// Execute the pipeline // Execute the pipeline
println!("Executing pipeline...\n"); println!("Executing pipeline...\n");
+11 -38
View File
@@ -88,7 +88,7 @@ check_pass "Zero clippy warnings"
echo "" echo ""
echo "→ Checking for TODO/FIXME/XXX/HACK in source..." echo "→ Checking for TODO/FIXME/XXX/HACK in source..."
TODO_COUNT=$(grep -r "TODO\|FIXME\|XXX\|HACK" src/ dagx-macros/src/ 2>/dev/null | wc -l) TODO_COUNT=$(grep -r "TODO\|FIXME\|XXX\|HACK" src/ dagx-macros/src/ dagx-test/src/ 2>/dev/null | wc -l)
if [ "$TODO_COUNT" -gt 0 ]; then if [ "$TODO_COUNT" -gt 0 ]; then
check_warn "Found $TODO_COUNT TODO/FIXME/HACK comments in source" check_warn "Found $TODO_COUNT TODO/FIXME/HACK comments in source"
else else
@@ -209,6 +209,7 @@ echo ""
echo "→ Verifying LICENSE..." echo "→ Verifying LICENSE..."
[ -f LICENSE ] || check_fail "LICENSE missing" [ -f LICENSE ] || check_fail "LICENSE missing"
[ -f dagx-macros/LICENSE ] || check_fail "dagx-macros/LICENSE missing" [ -f dagx-macros/LICENSE ] || check_fail "dagx-macros/LICENSE missing"
[ -f dagx-test/LICENSE ] || check_fail "dagx-test/LICENSE missing"
check_pass "LICENSE files present in both crates" check_pass "LICENSE files present in both crates"
# ============================================================================= # =============================================================================
@@ -259,45 +260,13 @@ echo "→ Verifying benchmarks compile..."
cargo bench --no-run || check_fail "Benchmarks failed to compile" cargo bench --no-run || check_fail "Benchmarks failed to compile"
check_pass "Benchmarks compile successfully" check_pass "Benchmarks compile successfully"
# =============================================================================
# CARGO.TOML METADATA VERIFICATION
# =============================================================================
section "CARGO.TOML METADATA"
echo "→ Checking dagx Cargo.toml..."
grep -q 'name = "dagx"' Cargo.toml || check_fail "Package name incorrect"
grep -q "version = \"$VERSION\"" Cargo.toml || check_fail "Version not $VERSION"
grep -q 'edition = "2021"' Cargo.toml || check_fail "Edition not 2021"
grep -q 'license = "MIT"' Cargo.toml || check_fail "License not MIT"
grep -q 'description = "A minimal, type-safe' Cargo.toml || check_fail "Description missing"
grep -q 'repository = "https://github.com/swaits/dagx"' Cargo.toml || check_fail "Repository URL incorrect"
grep -q 'homepage = "https://github.com/swaits/dagx"' Cargo.toml || check_fail "Homepage URL incorrect"
grep -q 'documentation = "https://docs.rs/dagx"' Cargo.toml || check_fail "Documentation URL incorrect"
grep -q 'readme = "README.md"' Cargo.toml || check_fail "README not specified"
grep -q 'keywords = \["dag"' Cargo.toml || check_fail "Keywords missing"
grep -q 'categories = \["asynchronous"' Cargo.toml || check_fail "Categories missing"
check_pass "dagx Cargo.toml metadata complete and correct"
echo ""
echo "→ Checking dagx-macros Cargo.toml..."
grep -q 'name = "dagx-macros"' dagx-macros/Cargo.toml || check_fail "dagx-macros name incorrect"
grep -q "version = \"$VERSION\"" dagx-macros/Cargo.toml || check_fail "dagx-macros version not $VERSION"
grep -q 'edition = "2021"' dagx-macros/Cargo.toml || check_fail "dagx-macros edition not 2021"
grep -q 'license = "MIT"' dagx-macros/Cargo.toml || check_fail "dagx-macros license not MIT"
grep -q 'description = "Procedural macros for dagx"' dagx-macros/Cargo.toml || check_fail "dagx-macros description missing"
grep -q 'repository = "https://github.com/swaits/dagx"' dagx-macros/Cargo.toml || check_fail "dagx-macros repository URL incorrect"
grep -q 'homepage = "https://github.com/swaits/dagx"' dagx-macros/Cargo.toml || check_fail "dagx-macros homepage URL incorrect"
grep -q 'documentation = "https://docs.rs/dagx-macros"' dagx-macros/Cargo.toml || check_fail "dagx-macros documentation URL incorrect"
grep -q 'readme = "README.md"' dagx-macros/Cargo.toml || check_fail "dagx-macros README not specified"
check_pass "dagx-macros Cargo.toml metadata complete and correct"
echo "" echo ""
echo "→ Verifying version consistency..." echo "→ Verifying version consistency..."
DAGX_VERSION=$(grep '^version = ' Cargo.toml | head -1 | cut -d'"' -f2) DAGX_VERSION=$(grep '^version = ' Cargo.toml | head -1 | cut -d'"' -f2)
MACROS_VERSION=$(grep '^version = ' dagx-macros/Cargo.toml | head -1 | cut -d'"' -f2) MACROS_VERSION=$(grep '^version = ' dagx-macros/Cargo.toml | head -1 | cut -d'"' -f2)
if [ "$DAGX_VERSION" != "$MACROS_VERSION" ]; then TEST_VERSION=$(grep '^version = ' dagx-test/Cargo.toml | head -1 | cut -d'"' -f2)
check_fail "Version mismatch: dagx=$DAGX_VERSION, dagx-macros=$MACROS_VERSION" if [ "$DAGX_VERSION" != "$MACROS_VERSION" ] || [ "$DAGX_VERSION" != "$TEST_VERSION" ]; then
check_fail "Version mismatch: dagx=$DAGX_VERSION, dagx-macros=$MACROS_VERSION, dagx-test=$TEST_VERSION"
else else
check_pass "Versions match: both are $DAGX_VERSION" check_pass "Versions match: both are $DAGX_VERSION"
fi fi
@@ -344,7 +313,7 @@ fi
section "SECURITY CHECKS" section "SECURITY CHECKS"
echo "→ Checking for sensitive data..." echo "→ Checking for sensitive data..."
if grep -r "api[_-]key\|secret\|password\|token" src/ dagx-macros/src/ 2>/dev/null | grep -v "^Binary" | grep -v "token for a node" | grep -q .; then if grep -r "api[_-]key\|secret\|password\|token" src/ dagx-macros/src/ dagx-test/src 2>/dev/null | grep -v "^Binary" | grep -v "token for a node" | grep -q .; then
check_fail "Potential sensitive data found in source" check_fail "Potential sensitive data found in source"
else else
check_pass "No sensitive data found" check_pass "No sensitive data found"
@@ -387,7 +356,11 @@ echo "4. Then publish dagx:"
echo " cd .." echo " cd .."
echo " cargo publish" echo " cargo publish"
echo "" echo ""
echo "5. Push changes and bookmark:" echo "5. Then publish dagx-test:"
echo " cd dagx-test"
echo " cargo publish"
echo ""
echo "6. Push changes and bookmark:"
echo " jj git push" echo " jj git push"
echo "" echo ""
echo -e "${BLUE}Note: dagx-macros MUST be published before dagx${NC}" echo -e "${BLUE}Note: dagx-macros MUST be published before dagx${NC}"
-9
View File
@@ -212,13 +212,6 @@ impl<T> Clone for TaskHandle<T> {
impl<T> Copy for TaskHandle<T> {} impl<T> Copy for TaskHandle<T> {}
// TaskHandle can be converted from &TaskHandle (for .get() calls)
impl<T> From<&TaskHandle<T>> for TaskHandle<T> {
fn from(handle: &TaskHandle<T>) -> Self {
*handle
}
}
/// Takes a task and converts it to either a TaskBuilder or a TaskHandle, /// Takes a task and converts it to either a TaskBuilder or a TaskHandle,
/// depending on whether it has inputs or not. /// depending on whether it has inputs or not.
/// ///
@@ -269,8 +262,6 @@ macro_rules! impl_wire_tuple {
}; };
} }
// Generate DepsTuple implementations for tuples of size 2-8.
// Supporting up to 8 elements covers the vast majority of use cases.
impl_wire_tuple!(T1); impl_wire_tuple!(T1);
impl_wire_tuple!(T1, T2); impl_wire_tuple!(T1, T2);
impl_wire_tuple!(T1, T2, T3); impl_wire_tuple!(T1, T2, T3);
+9 -21
View File
@@ -21,21 +21,9 @@ impl DepsTuple<()> for () {
} }
// Implementation for single dependency (all forms) // Implementation for single dependency (all forms)
impl<T, O> DepsTuple<(O,)> for T impl<O> DepsTuple<(O,)> for TaskHandle<O> {
where
T: Into<TaskHandle<O>>,
{
fn to_node_ids(self) -> Vec<NodeId> { fn to_node_ids(self) -> Vec<NodeId> {
vec![self.into().id] vec![self.id]
}
}
impl<T, O> DepsTuple<O> for (T,)
where
T: Into<TaskHandle<O>>,
{
fn to_node_ids(self) -> Vec<NodeId> {
vec![self.0.into().id]
} }
} }
@@ -47,27 +35,27 @@ where
/// This allows flexible dependency specification: /// This allows flexible dependency specification:
/// ```ignore /// ```ignore
/// // Using handles /// // Using handles
/// task.depends_on((&handle_a, &handle_b)) /// task.depends_on((handle_a, handle_b))
/// ///
/// // Using builders directly /// // Using builders directly
/// task.depends_on((&dag.add_task(TaskA), &dag.add_task(TaskB))) /// task.depends_on((dag.add_task(TaskA), dag.add_task(TaskB)))
/// ``` /// ```
macro_rules! impl_deps_tuple { macro_rules! impl_deps_tuple {
($($T:ident : $O:ident),+) => { ($($T:ident : $O:ident),+) => {
// For tuples of any combination of &TaskHandle, TaskHandle, and TaskBuilder impl<$($O),+> DepsTuple<($($O,)+)> for ($(TaskHandle<$O>,)+)
impl<$($T: Into<TaskHandle<$O>>, $O),+> DepsTuple<($($O,)+)> for ($($T,)+)
{ {
#[allow(non_snake_case)] #[allow(non_snake_case)]
fn to_node_ids(self) -> Vec<NodeId> { fn to_node_ids(self) -> Vec<NodeId> {
let ($($T,)+) = self; let ($($O,)+) = self;
vec![$($T.into().id,)+] vec![$($O.id,)+]
} }
} }
}; };
} }
// Generate DepsTuple implementations for tuples of size 2-8. // Generate DepsTuple implementations for tuples of size 1-8.
// Supporting up to 8 elements covers the vast majority of use cases. // Supporting up to 8 elements covers the vast majority of use cases.
impl_deps_tuple!(T1:O1);
impl_deps_tuple!(T1:O1, T2:O2); impl_deps_tuple!(T1:O1, T2:O2);
impl_deps_tuple!(T1:O1, T2:O2, T3:O3); impl_deps_tuple!(T1:O1, T2:O2, T3:O3);
impl_deps_tuple!(T1:O1, T2:O2, T3:O3, T4:O4); impl_deps_tuple!(T1:O1, T2:O2, T3:O3, T4:O4);
+6 -28
View File
@@ -57,7 +57,7 @@ fn test_deps_tuple_single_handle_tuple_ref() {
id: NodeId(7), id: NodeId(7),
_phantom: std::marker::PhantomData, _phantom: std::marker::PhantomData,
}; };
let node_ids = (&handle,).to_node_ids(); let node_ids = (handle,).to_node_ids();
assert_eq!(node_ids.len(), 1); assert_eq!(node_ids.len(), 1);
assert_eq!(node_ids[0], NodeId(7)); assert_eq!(node_ids[0], NodeId(7));
} }
@@ -114,7 +114,7 @@ fn test_deps_tuple_multiple_handles() {
}; };
// Test 2-tuple // Test 2-tuple
let node_ids = (&handle1, &handle2).to_node_ids(); let node_ids = (handle1, handle2).to_node_ids();
assert_eq!(node_ids.len(), 2); assert_eq!(node_ids.len(), 2);
assert_eq!(node_ids[0], NodeId(1)); assert_eq!(node_ids[0], NodeId(1));
assert_eq!(node_ids[1], NodeId(2)); assert_eq!(node_ids[1], NodeId(2));
@@ -124,7 +124,7 @@ fn test_deps_tuple_multiple_handles() {
id: NodeId(3), id: NodeId(3),
_phantom: std::marker::PhantomData, _phantom: std::marker::PhantomData,
}; };
let node_ids = (&handle1, &handle2, &handle3).to_node_ids(); let node_ids = (handle1, handle2, handle3).to_node_ids();
assert_eq!(node_ids.len(), 3); assert_eq!(node_ids.len(), 3);
assert_eq!(node_ids[0], NodeId(1)); assert_eq!(node_ids[0], NodeId(1));
assert_eq!(node_ids[1], NodeId(2)); assert_eq!(node_ids[1], NodeId(2));
@@ -162,7 +162,7 @@ fn test_deps_tuple_large_tuples() {
} }
// Test 4-tuple // Test 4-tuple
let node_ids = (&handles[0], &handles[1], &handles[2], &handles[3]).to_node_ids(); let node_ids = (handles[0], handles[1], handles[2], handles[3]).to_node_ids();
assert_eq!(node_ids.len(), 4); assert_eq!(node_ids.len(), 4);
for (i, node_id) in node_ids.iter().enumerate().take(4) { for (i, node_id) in node_ids.iter().enumerate().take(4) {
assert_eq!(*node_id, NodeId(i as u32)); assert_eq!(*node_id, NodeId(i as u32));
@@ -170,14 +170,8 @@ fn test_deps_tuple_large_tuples() {
// Test 8-tuple // Test 8-tuple
let node_ids = ( let node_ids = (
&handles[0], handles[0], handles[1], handles[2], handles[3], handles[4], handles[5], handles[6],
&handles[1], handles[7],
&handles[2],
&handles[3],
&handles[4],
&handles[5],
&handles[6],
&handles[7],
) )
.to_node_ids(); .to_node_ids();
assert_eq!(node_ids.len(), 8); assert_eq!(node_ids.len(), 8);
@@ -185,19 +179,3 @@ fn test_deps_tuple_large_tuples() {
assert_eq!(*node_id, NodeId(i as u32)); assert_eq!(*node_id, NodeId(i as u32));
} }
} }
#[test]
fn test_deps_handle_ref_explicit() {
// Test explicit &TaskHandle DepsTuple impl (line 27-28 in deps.rs)
let handle: TaskHandle<i32> = TaskHandle {
id: NodeId(77),
_phantom: std::marker::PhantomData,
};
// Create a reference and explicitly call to_node_ids
let handle_ref = &handle;
let node_ids = DepsTuple::<(i32,)>::to_node_ids(handle_ref);
assert_eq!(node_ids.len(), 1);
assert_eq!(node_ids[0], NodeId(77));
}
+6 -5
View File
@@ -320,8 +320,8 @@
//! let mut dag = DagRunner::new(); //! let mut dag = DagRunner::new();
//! //!
//! let base = dag.add_task(Value(10)).into(); //! let base = dag.add_task(Value(10)).into();
//! let plus1 = dag.add_task(Add(1)).depends_on(&base); //! let plus1 = dag.add_task(Add(1)).depends_on(base);
//! let times2 = dag.add_task(Scale(2)).depends_on(&base); //! let times2 = dag.add_task(Scale(2)).depends_on(base);
//! //!
//!let mut output = dag.run(|fut| async move { tokio::spawn(fut).await.unwrap() }).await.unwrap(); //!let mut output = dag.run(|fut| async move { tokio::spawn(fut).await.unwrap() }).await.unwrap();
//! //!
@@ -656,9 +656,9 @@
//! let data = dag.add_task(FetchData).into(); //! let data = dag.add_task(FetchData).into();
//! //!
//! // All three tasks get efficient Arc-wrapped sharing automatically //! // All three tasks get efficient Arc-wrapped sharing automatically
//! let task1 = dag.add_task(ProcessData).depends_on(&data); //! let task1 = dag.add_task(ProcessData).depends_on(data);
//! let task2 = dag.add_task(ProcessData).depends_on(&data); //! let task2 = dag.add_task(ProcessData).depends_on(data);
//! let task3 = dag.add_task(ProcessData).depends_on(&data); //! let task3 = dag.add_task(ProcessData).depends_on(data);
//! //!
//!let mut output = dag.run(|fut| async move { tokio::spawn(fut).await.unwrap() }).await.unwrap(); //!let mut output = dag.run(|fut| async move { tokio::spawn(fut).await.unwrap() }).await.unwrap();
//! # }; //! # };
@@ -784,4 +784,5 @@ pub use runner::DagRunner;
pub use task::{Task, TaskInput}; pub use task::{Task, TaskInput};
// Re-export the procedural macro // Re-export the procedural macro
#[cfg(feature = "derive")]
pub use dagx_macros::task; pub use dagx_macros::task;
+1 -4
View File
@@ -51,10 +51,7 @@ impl DagOutput {
/// assert_eq!(output.get(task).unwrap(), 42); /// assert_eq!(output.get(task).unwrap(), 42);
/// # }; /// # };
/// ``` /// ```
pub fn get<T: 'static + Send + Sync, H>(&mut self, handle: H) -> DagResult<T> pub fn get<T: 'static + Send + Sync>(&mut self, handle: TaskHandle<T>) -> DagResult<T> {
where
H: Into<TaskHandle<T>>,
{
let handle: TaskHandle<T> = handle.into(); let handle: TaskHandle<T> = handle.into();
let arc_output = self let arc_output = self
+6 -6
View File
@@ -460,13 +460,13 @@ impl DagRunner {
} }
} }
} }
}
// Return first error if any // Return first error if any
if let Some(err) = first_error { if let Some(err) = first_error {
#[cfg(feature = "tracing")] #[cfg(feature = "tracing")]
error!(?err, "DAG execution failed"); error!(?err, "DAG execution failed");
return Err(err); return Err(err);
}
} }
#[cfg(feature = "tracing")] #[cfg(feature = "tracing")]
-9
View File
@@ -140,14 +140,5 @@ impl_task_input!(A, B, C, D, E, F);
impl_task_input!(A, B, C, D, E, F, G); impl_task_input!(A, B, C, D, E, F, G);
impl_task_input!(A, B, C, D, E, F, G, H); impl_task_input!(A, B, C, D, E, F, G, H);
impl TaskInput<'static, ()> {
pub fn empty() -> Self {
Self {
inputs: [].iter(),
phantom: PhantomData,
}
}
}
#[cfg(test)] #[cfg(test)]
mod tests; mod tests;
-6
View File
@@ -32,9 +32,3 @@ async fn test_custom_task_implementation() {
// Can't call run again since task was consumed // Can't call run again since task was consumed
// This demonstrates the ownership model // This demonstrates the ownership model
} }
#[test]
fn test_empty_task_input() {
let empty = TaskInput::empty();
assert_eq!(empty.inputs.len(), 0);
}
+11 -11
View File
@@ -41,14 +41,14 @@ async fn test_multiple_parallel_deep_chains() -> DagResult<()> {
}, },
)) ))
.depends_on(( .depends_on((
&chain_ends[0], chain_ends[0],
&chain_ends[1], chain_ends[1],
&chain_ends[2], chain_ends[2],
&chain_ends[3], chain_ends[3],
&chain_ends[4], chain_ends[4],
&chain_ends[5], chain_ends[5],
&chain_ends[6], chain_ends[6],
&chain_ends[7], chain_ends[7],
)); ));
let mut output = dag let mut output = dag
@@ -120,7 +120,7 @@ async fn test_deep_binary_tree() -> DagResult<()> {
dag.add_task(task_fn::<(i32, i32), _, _>(move |(l, r): (&i32, &i32)| { dag.add_task(task_fn::<(i32, i32), _, _>(move |(l, r): (&i32, &i32)| {
l + r l + r
})) }))
.depends_on((&left, &right)) .depends_on((left, right))
} }
} }
@@ -152,7 +152,7 @@ async fn test_fibonacci_chain() -> DagResult<()> {
for _ in 2..50 { for _ in 2..50 {
let next = dag let next = dag
.add_task(task_fn::<(i64, i64), _, _>(|(a, b): (&i64, &i64)| a + b)) .add_task(task_fn::<(i64, i64), _, _>(|(a, b): (&i64, &i64)| a + b))
.depends_on((&prev2, &prev1)); .depends_on((prev2, prev1));
prev2 = prev1; prev2 = prev1;
prev1 = next; prev1 = next;
@@ -196,7 +196,7 @@ async fn test_zigzag_dependencies() -> DagResult<()> {
// Final task depends on both chains // Final task depends on both chains
let final_task = dag let final_task = dag
.add_task(task_fn::<(i32, i32), _, _>(|(a, b): (&i32, &i32)| a + b)) .add_task(task_fn::<(i32, i32), _, _>(|(a, b): (&i32, &i32)| a + b))
.depends_on((&chain_a, &chain_b)); .depends_on((chain_a, chain_b));
let mut output = dag let mut output = dag
.run(|fut| async move { tokio::spawn(fut).await.unwrap() }) .run(|fut| async move { tokio::spawn(fut).await.unwrap() })
+1 -1
View File
@@ -267,7 +267,7 @@ async fn test_type_safety_prevents_invalid_graphs() {
let t2 = dag.add_task(Transform).depends_on(s2); let t2 = dag.add_task(Transform).depends_on(s2);
// Aggregate depends on transforms // Aggregate depends on transforms
let result = dag.add_task(Aggregate).depends_on((&t1, &t2)); let result = dag.add_task(Aggregate).depends_on((t1, t2));
// Execute and verify // Execute and verify
let mut output = dag let mut output = dag
+5 -5
View File
@@ -54,7 +54,7 @@ async fn test_four_dependencies() -> DagResult<()> {
.add_task(task_fn::<(i32, i32, i32, i32), _, _>( .add_task(task_fn::<(i32, i32, i32, i32), _, _>(
|(a, b, c, d): (&i32, &i32, &i32, &i32)| a + b + c + d, |(a, b, c, d): (&i32, &i32, &i32, &i32)| a + b + c + d,
)) ))
.depends_on((&deps[0], &deps[1], &deps[2], &deps[3])); .depends_on((deps[0], deps[1], deps[2], deps[3]));
let mut output = dag let mut output = dag
.run(|fut| async move { tokio::spawn(fut).await.unwrap() }) .run(|fut| async move { tokio::spawn(fut).await.unwrap() })
@@ -76,7 +76,7 @@ async fn test_five_dependencies() -> DagResult<()> {
.add_task(task_fn::<(i32, i32, i32, i32, i32), _, _>( .add_task(task_fn::<(i32, i32, i32, i32, i32), _, _>(
|(a, b, c, d, e): (&i32, &i32, &i32, &i32, &i32)| a + b + c + d + e, |(a, b, c, d, e): (&i32, &i32, &i32, &i32, &i32)| a + b + c + d + e,
)) ))
.depends_on((&deps[0], &deps[1], &deps[2], &deps[3], &deps[4])); .depends_on((deps[0], deps[1], deps[2], deps[3], deps[4]));
let mut output = dag let mut output = dag
.run(|fut| async move { tokio::spawn(fut).await.unwrap() }) .run(|fut| async move { tokio::spawn(fut).await.unwrap() })
@@ -98,7 +98,7 @@ async fn test_six_dependencies() -> DagResult<()> {
.add_task(task_fn::<(i32, i32, i32, i32, i32, i32), _, _>( .add_task(task_fn::<(i32, i32, i32, i32, i32, i32), _, _>(
|(a, b, c, d, e, f): (&i32, &i32, &i32, &i32, &i32, &i32)| a + b + c + d + e + f, |(a, b, c, d, e, f): (&i32, &i32, &i32, &i32, &i32, &i32)| a + b + c + d + e + f,
)) ))
.depends_on((&deps[0], &deps[1], &deps[2], &deps[3], &deps[4], &deps[5])); .depends_on((deps[0], deps[1], deps[2], deps[3], deps[4], deps[5]));
let mut output = dag let mut output = dag
.run(|fut| async move { tokio::spawn(fut).await.unwrap() }) .run(|fut| async move { tokio::spawn(fut).await.unwrap() })
@@ -123,7 +123,7 @@ async fn test_seven_dependencies() -> DagResult<()> {
}, },
)) ))
.depends_on(( .depends_on((
&deps[0], &deps[1], &deps[2], &deps[3], &deps[4], &deps[5], &deps[6], deps[0], deps[1], deps[2], deps[3], deps[4], deps[5], deps[6],
)); ));
let mut output = dag let mut output = dag
@@ -149,7 +149,7 @@ async fn test_eight_dependencies() -> DagResult<()> {
}, },
)) ))
.depends_on(( .depends_on((
&deps[0], &deps[1], &deps[2], &deps[3], &deps[4], &deps[5], &deps[6], &deps[7], deps[0], deps[1], deps[2], deps[3], deps[4], deps[5], deps[6], deps[7],
)); ));
let mut output = dag let mut output = dag
+1 -1
View File
@@ -53,7 +53,7 @@ async fn test_diamond_error_propagation() {
.depends_on(source); .depends_on(source);
dag.add_task(task_fn::<(i32, i32), _, _>(|(l, r): (&i32, &i32)| l + r)) dag.add_task(task_fn::<(i32, i32), _, _>(|(l, r): (&i32, &i32)| l + r))
.depends_on((&left, &right)); .depends_on((left, right));
assert!(matches!( assert!(matches!(
dag.run(|fut| async move { tokio::spawn(fut).await.unwrap() }) dag.run(|fut| async move { tokio::spawn(fut).await.unwrap() })
+1 -1
View File
@@ -54,7 +54,7 @@ async fn test_diamond_dependency() {
.depends_on(a); .depends_on(a);
let d = dag let d = dag
.add_task(task_fn::<(i32, i32), _, _>(|(x, y): (&i32, &i32)| x + y)) .add_task(task_fn::<(i32, i32), _, _>(|(x, y): (&i32, &i32)| x + y))
.depends_on((&b, &c)); .depends_on((b, c));
let mut output = dag let mut output = dag
.run(|fut| async move { tokio::spawn(fut).await.unwrap() }) .run(|fut| async move { tokio::spawn(fut).await.unwrap() })
+2 -2
View File
@@ -255,7 +255,7 @@ async fn test_layered_interleaving() -> DagResult<()> {
dag.add_task(Layer23Task { dag.add_task(Layer23Task {
active: layer_active.clone(), active: layer_active.clone(),
}) })
.depends_on((&layer1[idx], &layer1[idx + 1])) .depends_on((layer1[idx], layer1[idx + 1]))
}) })
.collect(); .collect();
@@ -264,7 +264,7 @@ async fn test_layered_interleaving() -> DagResult<()> {
.add_task(Layer23Task { .add_task(Layer23Task {
active: layer_active.clone(), active: layer_active.clone(),
}) })
.depends_on((&layer2[0], &layer2[1])); .depends_on((layer2[0], layer2[1]));
let mut output = dag let mut output = dag
.run(|fut| async move { tokio::spawn(fut).await.unwrap() }) .run(|fut| async move { tokio::spawn(fut).await.unwrap() })
+1 -1
View File
@@ -140,7 +140,7 @@ async fn test_layers_execute_in_parallel() -> DagResult<()> {
a + b + c a + b + c
}, },
)) ))
.depends_on((&layer1[0], &layer1[1], &layer1[2])) .depends_on((layer1[0], layer1[1], layer1[2]))
}; };
let mut output = dag let mut output = dag