pub struct Workspace<CS: StateStoreBackend, A: AgentBackend, X: ActionBackend = NoActions, C: ConditionBackend = NoConditions, T: StateTyping = JsonState, H: LifecycleHooks = NoHooks, E: ChildExecutor = InlineExecutor> { /* private fields */ }Expand description
An application workspace handle.
A typed projection into the owning App generation. Dropping it does not delete or close lower workspaces. Replacement uses a complete configuration and a revision fence; successful deletion revokes this projection. Retained terminal history can still be opened through its workflow after workspace deletion.
§Example
let snapshot = workspace.snapshot().await?;
assert_eq!(&snapshot.id, workspace.id());
assert_eq!(snapshot.revision, 1);Implementations§
Source§impl<CS: StateStoreBackend, A: AgentBackend, X: ActionBackend, C: ConditionBackend, T: StateTyping, H: LifecycleHooks, E: ChildExecutor> Workspace<CS, A, X, C, T, H, E>
impl<CS: StateStoreBackend, A: AgentBackend, X: ActionBackend, C: ConditionBackend, T: StateTyping, H: LifecycleHooks, E: ChildExecutor> Workspace<CS, A, X, C, T, H, E>
Sourcepub fn id(&self) -> &WorkspaceId
pub fn id(&self) -> &WorkspaceId
Returns this workspace’s stable identity.
§Example
let id = workspace.id().clone();
assert_eq!(workspace.snapshot().await?.id, id);Examples found in repository?
390 async fn seed(
391 &self,
392 app: &App<StateStore, Agent, WorkloadAction>,
393 actions: u64,
394 linear: bool,
395 ) -> Result<Value, Box<dyn Error + Send + Sync>> {
396 let workspace = app
397 .create_workspace(WorkspaceConfig {
398 name: Some("performance consumer".into()),
399 root: self.root.join("workspace"),
400 })
401 .await?;
402 let workflow = workspace.workflow("performance".parse()?).await?;
403 let mut run = workflow.prepare(WorkflowState::default()).await?;
404 let mut execution = run
405 .execution(ExecutionOptions {
406 halt_after: if linear {
407 Vec::new()
408 } else {
409 vec!["count".into()]
410 },
411 ..ExecutionOptions::default()
412 })
413 .await?;
414 let mut samples = Vec::with_capacity(actions as usize);
415 let started = Instant::now();
416 for ordinal in 0..actions {
417 let step_started = Instant::now();
418 let event = execution.step().await?;
419 samples.push(step_started.elapsed());
420 let Some(ExecutionEvent::Committed { outcomes, .. }) = event else {
421 panic!("each serial workload step must commit one action");
422 };
423 assert_eq!(outcomes.len(), 1);
424 assert_eq!(outcomes[0].ordinal, ordinal);
425 assert_eq!(
426 outcomes[0].node,
427 if linear {
428 format!("n{ordinal:04}")
429 } else {
430 "count".into()
431 }
432 );
433 assert!(outcomes[0].result.is_empty());
434 if !linear && ordinal == 0 {
435 // One public halt and explicit resume add two control rows to
436 // Initial + three checkpoints per action + Terminal.
437 let halted = execution.finish().await?;
438 assert!(matches!(&halted.status, RunStatus::Halted { node, result }
439 if node == "count" && result == &Some(Map::new())));
440 assert_eq!(halted.checkpoint, 4);
441 assert_eq!(
442 halted.state.values(),
443 &Map::from_iter([("count".into(), json!(1))])
444 );
445 assert_eq!(halted.last_action_ordinal, Some(0));
446 assert!(halted.outcomes == outcomes);
447 assert!(halted.interactions.is_empty());
448 execution = run.execution(ExecutionOptions::default()).await?;
449 }
450 }
451 let snapshot = execution.finish().await?;
452 Self::verify_snapshot(&snapshot, actions);
453 let execution_elapsed = started.elapsed();
454 let history = self
455 .history(&run, actions, linear, snapshot.checkpoint)
456 .await?;
457 if let Command::SeedHistory { rows } = self.command {
458 assert_eq!(history["rows"], rows);
459 }
460 Ok(
461 json!({"dataset":if linear {"linear"} else {"history"},"actions":actions,
462 "workspace":workspace.id().to_string(),"run":run.id().to_string(),
463 "occurrence":snapshot.occurrence,
464 "expected_state":snapshot.state.values(),"last_action_ordinal":snapshot.last_action_ordinal,
465 "terminal_checkpoint":snapshot.checkpoint,"retained_rows":history["rows"],
466 "execution_ns":execution_elapsed.as_nanos(),
467 "actions_per_second":actions as f64 / execution_elapsed.as_secs_f64(),
468 "step_latency":Self::samples(samples),"history":history}),
469 )
470 }More examples
63 async fn create_fork(
64 &self,
65 app: &App<StateStore, Agent>,
66 ) -> Result<(WorkspaceId, RunSelector, RunSelector), Box<dyn Error + Send + Sync>> {
67 let workspace = app
68 .create_workspace(WorkspaceConfig {
69 name: Some("history example".into()),
70 root: std::env::current_dir()?,
71 })
72 .await?;
73 let workflow = workspace.workflow("history-counter".parse()?).await?;
74 let mut source = workflow.prepare(WorkflowState::default()).await?;
75 let completed = source
76 .execution(ExecutionOptions::default())
77 .await?
78 .finish()
79 .await?;
80 if !matches!(completed.status, RunStatus::Succeeded)
81 || completed.state.values()["count"] != 2
82 {
83 return Err(
84 AppError::Integration("source did not complete both increments".into()).into(),
85 );
86 }
87
88 // An action ordinal selects the whole committed boundary containing it;
89 // it is independent of the checkpoint sequence and storage revision.
90 let first = source.checkpoint(CheckpointSelector::Action(0)).await?;
91 if !matches!(first.kind, CheckpointKind::Action)
92 || first.position.as_deref() != Some("first")
93 || first.state.values()["count"] != 1
94 {
95 return Err(
96 AppError::Integration("first action history is inconsistent".into()).into(),
97 );
98 }
99 let source_selector = RunSelector::Exact {
100 id: source.id().clone(),
101 occurrence: first.occurrence.clone(),
102 };
103 let mut fork = workflow
104 .restore(RestoreOptions {
105 source: source_selector.clone(),
106 checkpoint: CheckpointSelector::Action(0),
107 overrides: WorkflowState::new(Map::from_iter([("count".into(), json!(40))]))?,
108 position: ResumePosition::AfterCompleted,
109 fallback: None,
110 })
111 .await?;
112 let parked = fork.snapshot().await?;
113 if !matches!(parked.status, RunStatus::Paused)
114 || parked.checkpoint != 0
115 || parked.state.values()["count"] != 40
116 || !parked.outcomes.is_empty()
117 {
118 return Err(
119 AppError::Integration("restore did not create a parked clean copy".into()).into(),
120 );
121 }
122
123 source.delete(completed.checkpoint).await?;
124 if !matches!(source.snapshot().await, Err(AppError::NotFound(_))) {
125 return Err(
126 AppError::Integration("explicit source deletion did not finish".into()).into(),
127 );
128 }
129 // Deletion does not invalidate the copied state. AfterCompleted skips
130 // the first increment, so only the second changes the override: 40 -> 41.
131 let completed = fork
132 .execution(ExecutionOptions::default())
133 .await?
134 .finish()
135 .await?;
136 if !matches!(completed.status, RunStatus::Succeeded)
137 || completed.state.values()["count"] != 41
138 || completed.last_action_ordinal != Some(1)
139 {
140 return Err(
141 AppError::Integration("fork resumed from the wrong boundary".into()).into(),
142 );
143 }
144 println!("Source count: 2; copied count: 40; completed fork count: 41");
145 Ok((
146 workspace.id().clone(),
147 source_selector,
148 RunSelector::Exact {
149 id: fork.id().clone(),
150 occurrence: completed.occurrence,
151 },
152 ))
153 }Sourcepub async fn snapshot(&self) -> Result<WorkspaceSnapshot, AppError>
pub async fn snapshot(&self) -> Result<WorkspaceSnapshot, AppError>
Loads the current workspace snapshot.
§Errors
Returns Closed after revocation or sealing, NotFound for absent coordination,
Conflict during unresolved lifecycle progress, or Integration for unreadable
committed evidence. A history-only workspace has no configuration snapshot.
§Example
let snapshot = workspace.snapshot().await?;
assert_eq!(snapshot.revision, 1);
assert_eq!(snapshot.config.name.as_deref(), Some("local counter"));Sourcepub async fn replace(
&self,
expected_revision: u64,
config: WorkspaceConfig,
) -> Result<WorkspaceSnapshot, AppError>
pub async fn replace( &self, expected_revision: u64, config: WorkspaceConfig, ) -> Result<WorkspaceSnapshot, AppError>
Atomically replaces the complete workspace configuration.
This is full replacement, including the name and root. Preflight checks all lower workspaces before fan-out. A partial lower failure retains the old App revision and durable reverse compensation; preflight never reserves a revision.
§Errors
Returns Conflict for a stale revision, a nonterminal run pinned to the current
revision, or partial lower fan-out (even if compensation succeeds). Invalid
configuration, capacity, revocation, storage/lower failures and bounded waits
report InvalidInput, Busy, Closed, Integration, or Timeout.
§Example
let before = workspace.snapshot().await?;
let after = workspace.replace(before.revision, WorkspaceConfig {
name: Some("reviewed".into()), root: before.config.root,
}).await?;
assert_eq!(after.revision, before.revision + 1);
assert_eq!(after.config.name.as_deref(), Some("reviewed"));Sourcepub async fn workflow(
&self,
id: WorkflowId,
) -> Result<Workflow<CS, A, X, C, T, H, E>, AppError>
pub async fn workflow( &self, id: WorkflowId, ) -> Result<Workflow<CS, A, X, C, T, H, E>, AppError>
Opens a configured workflow in this workspace.
Selects the current configured definition for new work. A deleted workspace may retain a history-only workflow when exact terminal evidence still exists.
§Errors
Returns NotFound for an unavailable definition or retained scope, Closed
for revoked admission, or Integration for inconsistent durable evidence.
§Example
let workflow = workspace.workflow("counter".parse()?).await?;
assert_eq!(workflow.id().to_string(), "counter");Examples found in repository?
390 async fn seed(
391 &self,
392 app: &App<StateStore, Agent, WorkloadAction>,
393 actions: u64,
394 linear: bool,
395 ) -> Result<Value, Box<dyn Error + Send + Sync>> {
396 let workspace = app
397 .create_workspace(WorkspaceConfig {
398 name: Some("performance consumer".into()),
399 root: self.root.join("workspace"),
400 })
401 .await?;
402 let workflow = workspace.workflow("performance".parse()?).await?;
403 let mut run = workflow.prepare(WorkflowState::default()).await?;
404 let mut execution = run
405 .execution(ExecutionOptions {
406 halt_after: if linear {
407 Vec::new()
408 } else {
409 vec!["count".into()]
410 },
411 ..ExecutionOptions::default()
412 })
413 .await?;
414 let mut samples = Vec::with_capacity(actions as usize);
415 let started = Instant::now();
416 for ordinal in 0..actions {
417 let step_started = Instant::now();
418 let event = execution.step().await?;
419 samples.push(step_started.elapsed());
420 let Some(ExecutionEvent::Committed { outcomes, .. }) = event else {
421 panic!("each serial workload step must commit one action");
422 };
423 assert_eq!(outcomes.len(), 1);
424 assert_eq!(outcomes[0].ordinal, ordinal);
425 assert_eq!(
426 outcomes[0].node,
427 if linear {
428 format!("n{ordinal:04}")
429 } else {
430 "count".into()
431 }
432 );
433 assert!(outcomes[0].result.is_empty());
434 if !linear && ordinal == 0 {
435 // One public halt and explicit resume add two control rows to
436 // Initial + three checkpoints per action + Terminal.
437 let halted = execution.finish().await?;
438 assert!(matches!(&halted.status, RunStatus::Halted { node, result }
439 if node == "count" && result == &Some(Map::new())));
440 assert_eq!(halted.checkpoint, 4);
441 assert_eq!(
442 halted.state.values(),
443 &Map::from_iter([("count".into(), json!(1))])
444 );
445 assert_eq!(halted.last_action_ordinal, Some(0));
446 assert!(halted.outcomes == outcomes);
447 assert!(halted.interactions.is_empty());
448 execution = run.execution(ExecutionOptions::default()).await?;
449 }
450 }
451 let snapshot = execution.finish().await?;
452 Self::verify_snapshot(&snapshot, actions);
453 let execution_elapsed = started.elapsed();
454 let history = self
455 .history(&run, actions, linear, snapshot.checkpoint)
456 .await?;
457 if let Command::SeedHistory { rows } = self.command {
458 assert_eq!(history["rows"], rows);
459 }
460 Ok(
461 json!({"dataset":if linear {"linear"} else {"history"},"actions":actions,
462 "workspace":workspace.id().to_string(),"run":run.id().to_string(),
463 "occurrence":snapshot.occurrence,
464 "expected_state":snapshot.state.values(),"last_action_ordinal":snapshot.last_action_ordinal,
465 "terminal_checkpoint":snapshot.checkpoint,"retained_rows":history["rows"],
466 "execution_ns":execution_elapsed.as_nanos(),
467 "actions_per_second":actions as f64 / execution_elapsed.as_secs_f64(),
468 "step_latency":Self::samples(samples),"history":history}),
469 )
470 }
471
472 async fn reopen(
473 &self,
474 app: &App<StateStore, Agent, WorkloadAction>,
475 manifest: &Value,
476 actions: u64,
477 open_only: bool,
478 ) -> Result<Value, Box<dyn Error + Send + Sync>> {
479 let started = Instant::now();
480 let workspace: WorkspaceId = manifest["workspace"]
481 .as_str()
482 .ok_or("missing workspace ID")?
483 .parse()?;
484 let run_id: RunId = manifest["run"].as_str().ok_or("missing run ID")?.parse()?;
485 let occurrence = manifest["occurrence"]
486 .as_str()
487 .ok_or("missing run occurrence")?
488 .to_owned();
489 let workspace = app.workspace(workspace).await?;
490 let workflow = workspace.workflow("performance".parse()?).await?;
491 let run = workflow
492 .open_run(RunSelector::Exact {
493 id: run_id,
494 occurrence: occurrence.clone(),
495 })
496 .await?;
497 let snapshot = run.snapshot().await?;
498 Self::verify_snapshot(&snapshot, actions);
499 assert_eq!(snapshot.occurrence, occurrence);
500 assert_eq!(json!(snapshot.checkpoint), manifest["terminal_checkpoint"]);
501 assert_eq!(json!(snapshot.state.values()), manifest["expected_state"]);
502 assert_eq!(
503 json!(snapshot.last_action_ordinal),
504 manifest["last_action_ordinal"]
505 );
506 let lookup = started.elapsed();
507 if open_only {
508 return Ok(
509 json!({"dataset":"history","actions":actions,"mode":"open-only",
510 "lookup_and_snapshot_ns":lookup.as_nanos(),
511 "seed_retained_rows":manifest["retained_rows"],"observed_history_rows":null}),
512 );
513 }
514 let history = self
515 .history(&run, actions, false, snapshot.checkpoint)
516 .await?;
517 assert_eq!(history["rows"], manifest["retained_rows"]);
518 Ok(
519 json!({"dataset":"history","actions":actions,"mode":"full-history",
520 "lookup_and_snapshot_ns":lookup.as_nanos(),"history":history}),
521 )
522 }
523
524 fn definitions(&self, actions: u64) -> Result<(Vec<WorkflowDefinition>, usize), AppError> {
525 if matches!(
526 self.command,
527 Command::Linear | Command::SeedHistory { .. } | Command::ReopenHistory { .. }
528 ) {
529 let (definition, bytes) =
530 Self::definition(matches!(self.command, Command::Linear), actions)?;
531 return Ok((vec![definition], bytes));
532 }
533 let leaf = json!({
534 "schema":"app-kit.workflow/v2","id":"performance-leaf","revision":"r1",
535 "state":{"value":0,"result":0},"required":["value","result"],
536 "reducers":{"value":"reject_concurrent","result":"reject_concurrent"},
537 "entry":"work","nodes":[{"id":"work","deadline_ms":NODE_DEADLINE_MS,
538 "step":{"kind":"action","action":"performance-action","revision":"r1"}}],"routes":{}
539 });
540 let documents = match self.command {
541 Command::Ready => {
542 let mut nodes = vec![json!({"id":"fork","step":{"kind":"state","mutations":[]}})];
543 let mut branches = Vec::new();
544 for index in 0..32 {
545 let node = format!("n{index:02}");
546 nodes.push(json!({"id":node,"deadline_ms":NODE_DEADLINE_MS,
547 "step":{"kind":"action","action":"performance-action","revision":"r1"}}));
548 branches.push(json!({"target":{"kind":"node","id":node}}));
549 }
550 vec![json!({
551 "schema":"app-kit.workflow/v2","id":"performance","revision":"r1",
552 "state":{"value":0,"result":0},"required":["value","result"],
553 "reducers":{"value":"reject_concurrent","result":"collect"},
554 "entry":"fork","nodes":nodes,
555 "routes":{"fork":{"mode":"broadcast","branches":branches}}
556 })]
557 }
558 Command::Map | Command::NestedCancel => {
559 let nested = matches!(self.command, Command::NestedCancel);
560 let items = if nested { 2 } else { 4_096 };
561 let mut documents = vec![
562 json!({
563 "schema":"app-kit.workflow/v2","id":"performance","revision":"r1",
564 "state":{"value":0,"result":0},"required":["value","result"],
565 "reducers":{"value":"reject_concurrent","result":"sum_integer"},
566 "entry":"map","nodes":[{"id":"map","deadline_ms":if nested {NODE_DEADLINE_MS} else {WORKLOAD_BUDGET.as_millis() as u64},
567 "step":{"kind":"map","workflow":if nested {"performance-mid"} else {"performance-leaf"},
568 "items":{"kind":"literal","value":(0..items).collect::<Vec<_>>()},
569 "input_key":"value","max_parallel":if nested {2} else {8},
570 "reductions":[{"source":"result","target":"result","reducer":"sum_integer"}]}}],"routes":{}
571 }),
572 leaf,
573 ];
574 if nested {
575 documents.push(json!({
576 "schema":"app-kit.workflow/v2","id":"performance-mid","revision":"r1",
577 "state":{"value":0,"result":0},"required":["value","result"],
578 "reducers":{"value":"reject_concurrent","result":"sum_integer"},
579 "entry":"map","nodes":[{"id":"map","deadline_ms":NODE_DEADLINE_MS,
580 "step":{"kind":"map","workflow":"performance-leaf",
581 "items":{"kind":"literal","value":(0..8).collect::<Vec<_>>()},
582 "input_key":"value","max_parallel":8,
583 "reductions":[{"source":"result","target":"result","reducer":"sum_integer"}]}}],"routes":{}
584 }));
585 }
586 documents
587 }
588 Command::Repeated | Command::DelayedObserver => vec![leaf],
589 Command::Linear | Command::SeedHistory { .. } | Command::ReopenHistory { .. } => {
590 unreachable!()
591 }
592 };
593 let mut bytes = 0;
594 let mut definitions = Vec::with_capacity(documents.len());
595 for document in documents {
596 let source = document.to_string();
597 assert!(source.len() <= 1_048_576);
598 bytes += source.len();
599 definitions.push(WorkflowDefinition::parse(&source)?);
600 }
601 Ok((definitions, bytes))
602 }
603
604 async fn ready(
605 &self,
606 app: &App<StateStore, Agent, WorkloadAction>,
607 probe: &WorkloadProbe,
608 ) -> Result<Value, Box<dyn Error + Send + Sync>> {
609 let workspace = app
610 .create_workspace(WorkspaceConfig {
611 name: None,
612 root: self.root.join("workspace"),
613 })
614 .await?;
615 let workflow = workspace.workflow("performance".parse()?).await?;
616 let mut waves = Vec::with_capacity(32);
617 let mut releases = Vec::with_capacity(32 * 31);
618 let mut barriers = Vec::with_capacity(32);
619 let started = Instant::now();
620 for permutation in 0..32 {
621 probe.assert_idle();
622 let mut run = workflow.prepare(WorkflowState::default()).await?;
623 let observer = workflow
624 .open_run(RunSelector::Latest(run.id().clone()))
625 .await?;
626 let mut execution = run.execution(ExecutionOptions::default()).await?;
627 let Some(ExecutionEvent::Committed { outcomes, .. }) = execution.step().await? else {
628 panic!("the native fork must commit before the ready wave");
629 };
630 assert_eq!(outcomes.len(), 1);
631 assert_eq!(
632 (outcomes[0].node.as_str(), outcomes[0].ordinal),
633 ("fork", 0)
634 );
635 let wave = Instant::now();
636 let mut finish = Box::pin(execution.finish());
637 tokio::select! {
638 _ = probe.wait_for(&probe.constructed, (permutation + 1) * 32) => {}
639 result = &mut finish => panic!("gated ready wave finished before admission: {}", result.is_ok()),
640 }
641 assert_eq!(probe.active.load(Ordering::SeqCst), 32);
642 let order = (0..32)
643 .map(|offset| (permutation + 31 - offset) % 32)
644 .collect::<Vec<_>>();
645 for (released, index) in order.iter().take(31).enumerate() {
646 let releasing = Instant::now();
647 probe.release(*index);
648 tokio::select! {
649 _ = probe.wait_for(&probe.completed, permutation * 32 + released + 1) => {}
650 result = &mut finish => panic!("a partial ready wave committed: {}", result.is_ok()),
651 }
652 releases.push(releasing.elapsed());
653 let visible = observer.snapshot().await?;
654 assert_eq!(
655 visible.state.values()["result"],
656 0,
657 "staged siblings cannot expose a partial barrier"
658 );
659 assert_eq!(visible.last_action_ordinal, Some(0));
660 }
661 let barrier = Instant::now();
662 probe.release(order[31]);
663 let snapshot = finish.await?;
664 barriers.push(barrier.elapsed());
665 waves.push(wave.elapsed());
666 assert!(matches!(snapshot.status, RunStatus::Succeeded));
667 assert!(snapshot.interactions.is_empty());
668 assert_eq!(
669 snapshot.state.values()["result"],
670 json!((1..=32).collect::<Vec<_>>())
671 );
672 assert_eq!(snapshot.last_action_ordinal, Some(32));
673 assert_eq!(snapshot.outcomes.len(), 32);
674 for (index, outcome) in snapshot.outcomes.iter().enumerate() {
675 assert_eq!(outcome.node, format!("n{index:02}"));
676 assert_eq!(outcome.ordinal, index as u64 + 1);
677 assert_eq!(
678 outcome.result,
679 Map::from_iter([("value".into(), json!(index + 1))])
680 );
681 }
682 assert_eq!(std::mem::take(&mut *probe.order.lock().unwrap()), order);
683 assert_eq!(
684 probe.completed.load(Ordering::SeqCst),
685 (permutation + 1) * 32
686 );
687 probe.assert_idle();
688 }
689 assert_eq!(probe.peak.load(Ordering::SeqCst), 32);
690 let elapsed = started.elapsed();
691 Ok(
692 json!({"dataset":"ready32","permutations":32,"ready_actions_per_wave":32,
693 "caller_action_commits":1024,"native_fork_commits":32,
694 "execution_ns":elapsed.as_nanos(),"caller_actions_per_second":1024.0 / elapsed.as_secs_f64(),
695 "wave_latency_including_snapshot_probes":Self::samples(waves),
696 "release_to_source_completion_latency":Self::samples(releases),
697 "last_release_to_committed_barrier_latency":Self::samples(barriers)}),
698 )
699 }
700
701 async fn mapped(
702 &self,
703 app: &App<StateStore, Agent, WorkloadAction>,
704 probe: &WorkloadProbe,
705 ) -> Result<Value, Box<dyn Error + Send + Sync>> {
706 let workspace = app
707 .create_workspace(WorkspaceConfig {
708 name: None,
709 root: self.root.join("workspace"),
710 })
711 .await?;
712 let workflow = workspace.workflow("performance".parse()?).await?;
713 let mut run = workflow.prepare(WorkflowState::default()).await?;
714 let observer = workflow
715 .open_run(RunSelector::Latest(run.id().clone()))
716 .await?;
717 let started = Instant::now();
718 let mut finish = Box::pin(run.execution(ExecutionOptions::default()).await?.finish());
719 tokio::select! {
720 _ = probe.wait_for(&probe.constructed, 8) => {}
721 result = &mut finish => panic!("map finished before its first eight blocked sources: {}", result.is_ok()),
722 }
723 assert_eq!(probe.active.load(Ordering::SeqCst), 8);
724 assert_eq!(probe.constructed.load(Ordering::SeqCst), 8);
725 let unchanged = observer.snapshot().await?;
726 assert_eq!(unchanged.state.values()["result"], 0);
727 assert!(unchanged.last_action_ordinal.is_none());
728 probe.release_all();
729 let snapshot = finish.await?;
730 let elapsed = started.elapsed();
731 assert!(matches!(snapshot.status, RunStatus::Succeeded));
732 assert!(snapshot.interactions.is_empty());
733 assert_eq!(snapshot.state.values()["result"], 4_096_u64 * 4_097 / 2);
734 assert_eq!(snapshot.last_action_ordinal, Some(0));
735 assert_eq!(snapshot.outcomes.len(), 1);
736 assert_eq!(snapshot.outcomes[0].node, "map");
737 assert_eq!(probe.constructed.load(Ordering::SeqCst), 4_096);
738 assert_eq!(probe.completed.load(Ordering::SeqCst), 4_096);
739 assert_eq!(probe.peak.load(Ordering::SeqCst), 8);
740 probe.assert_idle();
741 let child = workspace.workflow("performance-leaf".parse()?).await?;
742 let mut cursor = None;
743 let mut values = BTreeSet::new();
744 let verifying = Instant::now();
745 loop {
746 let page = child
747 .list_runs(cursor, NonZeroUsize::new(PAGE_SIZE).unwrap())
748 .await?;
749 assert!(page.len() <= PAGE_SIZE);
750 if page.is_empty() {
751 break;
752 }
753 for row in &page {
754 assert!(matches!(row.status, RunStatus::Succeeded));
755 let value = row.state.values()["value"].as_u64().unwrap();
756 assert!(
757 value < 4_096 && values.insert(value),
758 "every mapped input has exactly one retained child"
759 );
760 assert_eq!(row.state.values()["result"], value + 1);
761 assert_eq!(row.last_action_ordinal, Some(0));
762 assert_eq!(row.outcomes.len(), 1);
763 assert_eq!(row.outcomes[0].node, "work");
764 }
765 let last = page.last().unwrap();
766 cursor = Some(RunSelector::Exact {
767 id: last.id.clone(),
768 occurrence: last.occurrence.clone(),
769 });
770 assert!(values.len() <= 4_096);
771 }
772 assert_eq!(values.len(), 4_096);
773 Ok(json!({"dataset":"map4096","items":4096,"max_parallel":8,
774 "parent_node_deadline_ms":WORKLOAD_BUDGET.as_millis(),
775 "execution_ns":elapsed.as_nanos(),"child_actions_per_second":4096.0 / elapsed.as_secs_f64(),
776 "map_completion_latency":Self::samples(vec![elapsed]),
777 "child_inventory_verification_ns":verifying.elapsed().as_nanos(),"verified_children":values.len()}))
778 }
779
780 async fn repeated(
781 &self,
782 app: &App<StateStore, Agent, WorkloadAction>,
783 probe: &WorkloadProbe,
784 ) -> Result<Value, Box<dyn Error + Send + Sync>> {
785 let workspace = app
786 .create_workspace(WorkspaceConfig {
787 name: None,
788 root: self.root.join("workspace"),
789 })
790 .await?;
791 let workflow = workspace.workflow("performance-leaf".parse()?).await?;
792 let started = Instant::now();
793 // Admit one run at a time, then directly poll at most 16 original
794 // executions together. No consumer task is spawned or detached.
795 let mut runs = std::pin::pin!(
796 stream::iter(0..100_u64)
797 .then(|value| {
798 let workflow = &workflow;
799 async move {
800 let started = Instant::now();
801 let run = workflow
802 .prepare(WorkflowState::new(Map::from_iter([(
803 "value".into(),
804 json!(value),
805 )]))?)
806 .await?;
807 Ok::<_, AppError>((value, run, started))
808 }
809 })
810 .map(|prepared| async move {
811 let (value, mut run, started) = prepared?;
812 let snapshot = run
813 .execution(ExecutionOptions::default())
814 .await?
815 .finish()
816 .await?;
817 assert!(matches!(snapshot.status, RunStatus::Succeeded));
818 assert!(snapshot.interactions.is_empty());
819 assert_eq!(snapshot.state.values()["value"], value);
820 assert_eq!(snapshot.state.values()["result"], value + 1);
821 assert_eq!(snapshot.last_action_ordinal, Some(0));
822 assert_eq!(snapshot.outcomes.len(), 1);
823 Ok::<_, AppError>((run.id().clone(), started.elapsed()))
824 })
825 .buffer_unordered(16)
826 );
827 tokio::select! {
828 _ = probe.wait_for(&probe.constructed, 16) => {}
829 result = runs.next() => panic!(
830 "run finished before the first 16 blocked sources: {:?}",
831 result.map(|result| result.map(|_| ())),
832 ),
833 }
834 assert_eq!(probe.active.load(Ordering::SeqCst), 16);
835 assert_eq!(probe.constructed.load(Ordering::SeqCst), 16);
836 probe.release_all();
837 let mut ids = BTreeSet::new();
838 let mut samples = Vec::with_capacity(100);
839 while let Some(result) = runs.next().await {
840 let (id, elapsed) = result?;
841 assert!(ids.insert(id));
842 samples.push(elapsed);
843 }
844 assert_eq!(ids.len(), 100);
845 assert_eq!(probe.constructed.load(Ordering::SeqCst), 100);
846 assert_eq!(probe.completed.load(Ordering::SeqCst), 100);
847 assert_eq!(probe.peak.load(Ordering::SeqCst), 16);
848 probe.assert_idle();
849 let rows = workflow
850 .list_runs(None, NonZeroUsize::new(128).unwrap())
851 .await?;
852 assert_eq!(rows.len(), 100);
853 assert_eq!(
854 rows.iter()
855 .map(|row| row.id.clone())
856 .collect::<BTreeSet<_>>(),
857 ids
858 );
859 assert!(
860 rows.iter()
861 .all(|row| matches!(row.status, RunStatus::Succeeded))
862 );
863 let elapsed = started.elapsed();
864 Ok(
865 json!({"dataset":"repeated100","runs":100,"caller_in_flight_limit":16,"admission":"sequential",
866 "production_active_limit":"unchanged; this measures only the caller's 16-journey envelope",
867 "execution_ns":elapsed.as_nanos(),"runs_per_second":100.0 / elapsed.as_secs_f64(),
868 "run_latency_including_admission":Self::samples(samples),"indefinite_stability_proof":false}),
869 )
870 }
871
872 async fn nested_cancel(
873 &self,
874 app: &App<StateStore, Agent, WorkloadAction>,
875 probe: &WorkloadProbe,
876 ) -> Result<Value, Box<dyn Error + Send + Sync>> {
877 let workspace = app
878 .create_workspace(WorkspaceConfig {
879 name: None,
880 root: self.root.join("workspace"),
881 })
882 .await?;
883 let workflow = workspace.workflow("performance".parse()?).await?;
884 let run = workflow.start(ExecutionOptions::default()).await?;
885 tokio::select! {
886 _ = probe.wait_for(&probe.constructed, 16) => {}
887 result = run.wait() => panic!("nested run ended before its held leaf sources: {}", result.is_ok()),
888 }
889 assert_eq!(probe.active.load(Ordering::SeqCst), 16);
890 let cancelling = Instant::now();
891 let snapshot = tokio::time::timeout(Duration::from_secs(30), async {
892 loop {
893 let before = run.snapshot().await?;
894 assert_eq!(before.state.values()["result"], 0);
895 assert!(before.outcomes.is_empty() && before.last_action_ordinal.is_none());
896 match run.cancel(before.checkpoint).await {
897 Ok(_) => break,
898 Err(AppError::Conflict(_)) => tokio::task::yield_now().await,
899 Err(error) => return Err(error),
900 }
901 }
902 run.wait().await
903 })
904 .await
905 .map_err(|_| {
906 AppError::Timeout("nested cancellation exceeded its declared 30-second grace".into())
907 })??;
908 let elapsed = cancelling.elapsed();
909 assert!(matches!(snapshot.status, RunStatus::Cancelled));
910 assert!(snapshot.interactions.is_empty() && snapshot.outcomes.is_empty());
911 assert!(snapshot.last_action_ordinal.is_none());
912 assert_eq!(snapshot.state.values()["result"], 0);
913 assert_eq!(probe.constructed.load(Ordering::SeqCst), 16);
914 assert_eq!(probe.completed.load(Ordering::SeqCst), 0);
915 probe.assert_idle();
916 for (id, count) in [
917 ("performance", 1),
918 ("performance-mid", 2),
919 ("performance-leaf", 16),
920 ] {
921 let child = workspace.workflow(id.parse()?).await?;
922 let rows = child
923 .list_runs(None, NonZeroUsize::new(128).unwrap())
924 .await?;
925 assert_eq!(rows.len(), count);
926 for row in rows {
927 assert!(matches!(row.status, RunStatus::Cancelled));
928 assert!(row.outcomes.is_empty() && row.last_action_ordinal.is_none());
929 assert_eq!(row.state.values()["result"], 0);
930 }
931 }
932 Ok(
933 json!({"dataset":"nested-cancel","depth":2,"intermediate_children":2,"leaf_children":16,
934 "cooperative_cancel_grace_ms":30_000,"cancellation_latency":Self::samples(vec![elapsed]),
935 "source_replays":0,"caller_sources_dropped":16,"agent_unknown_replay":"not applicable: no Agent attempt"}),
936 )
937 }
938
939 async fn delayed_observer(
940 &self,
941 app: &App<StateStore, Agent, WorkloadAction>,
942 probe: &WorkloadProbe,
943 ) -> Result<Value, Box<dyn Error + Send + Sync>> {
944 let workspace = app
945 .create_workspace(WorkspaceConfig {
946 name: None,
947 root: self.root.join("workspace"),
948 })
949 .await?;
950 let workflow = workspace.workflow("performance-leaf".parse()?).await?;
951 let run = workflow.start(ExecutionOptions::default()).await?;
952 let observer = workflow
953 .open_run(RunSelector::Latest(run.id().clone()))
954 .await?;
955 // This public observation is deliberately never polled while the source
956 // produces its 256 partials and commits. It owns no execution work.
957 let delayed = observer.wait();
958 let produced = run.wait().await?;
959 assert!(matches!(produced.status, RunStatus::Succeeded));
960 assert_eq!(probe.completed.load(Ordering::SeqCst), 1);
961 probe.assert_idle();
962 tokio::time::sleep(Duration::from_secs(2)).await;
963 let observing = Instant::now();
964 let snapshot = delayed.await?;
965 let elapsed = observing.elapsed();
966 assert!(matches!(snapshot.status, RunStatus::Succeeded));
967 assert_eq!(snapshot.checkpoint, produced.checkpoint);
968 assert_eq!(snapshot.state.values()["result"], 1);
969 assert_eq!(snapshot.last_action_ordinal, Some(0));
970 assert_eq!(snapshot.outcomes.len(), 1);
971 assert_eq!(
972 snapshot.outcomes[0].result,
973 Map::from_iter([("value".into(), json!(1))])
974 );
975 assert_eq!(probe.constructed.load(Ordering::SeqCst), 1);
976 assert_eq!(probe.partials.load(Ordering::SeqCst), 256);
977 probe.assert_idle();
978 let terminal = run.snapshot().await?;
979 assert_eq!(terminal.checkpoint, snapshot.checkpoint);
980 assert_eq!(terminal.state, snapshot.state);
981 let mut cursor = None;
982 let mut actions = 0;
983 let mut rows = 0;
984 loop {
985 let page = run
986 .history(cursor, NonZeroUsize::new(PAGE_SIZE).unwrap())
987 .await?;
988 for row in &page.checkpoints {
989 rows += 1;
990 assert!(rows <= 32);
991 if matches!(row.kind, CheckpointKind::Action) {
992 actions += 1;
993 assert_eq!(row.action_ordinal, Some(0));
994 assert_eq!(row.outcomes.len(), 1);
995 }
996 }
997 cursor = page.next;
998 if cursor.is_none() {
999 break;
1000 }
1001 }
1002 assert_eq!(
1003 actions, 1,
1004 "partials cannot become extra durable action commits"
1005 );
1006 Ok(
1007 json!({"dataset":"delayed-observer","unobserved_partial_events":256,"delay_after_terminal_confirmation_ms":2_000,
1008 "observation_latency":Self::samples(vec![elapsed]),"retained_rows":rows,
1009 "memory_scope":"external process RSS; this consumer retains no partial payloads"}),
1010 )
1011 }More examples
63 async fn create_fork(
64 &self,
65 app: &App<StateStore, Agent>,
66 ) -> Result<(WorkspaceId, RunSelector, RunSelector), Box<dyn Error + Send + Sync>> {
67 let workspace = app
68 .create_workspace(WorkspaceConfig {
69 name: Some("history example".into()),
70 root: std::env::current_dir()?,
71 })
72 .await?;
73 let workflow = workspace.workflow("history-counter".parse()?).await?;
74 let mut source = workflow.prepare(WorkflowState::default()).await?;
75 let completed = source
76 .execution(ExecutionOptions::default())
77 .await?
78 .finish()
79 .await?;
80 if !matches!(completed.status, RunStatus::Succeeded)
81 || completed.state.values()["count"] != 2
82 {
83 return Err(
84 AppError::Integration("source did not complete both increments".into()).into(),
85 );
86 }
87
88 // An action ordinal selects the whole committed boundary containing it;
89 // it is independent of the checkpoint sequence and storage revision.
90 let first = source.checkpoint(CheckpointSelector::Action(0)).await?;
91 if !matches!(first.kind, CheckpointKind::Action)
92 || first.position.as_deref() != Some("first")
93 || first.state.values()["count"] != 1
94 {
95 return Err(
96 AppError::Integration("first action history is inconsistent".into()).into(),
97 );
98 }
99 let source_selector = RunSelector::Exact {
100 id: source.id().clone(),
101 occurrence: first.occurrence.clone(),
102 };
103 let mut fork = workflow
104 .restore(RestoreOptions {
105 source: source_selector.clone(),
106 checkpoint: CheckpointSelector::Action(0),
107 overrides: WorkflowState::new(Map::from_iter([("count".into(), json!(40))]))?,
108 position: ResumePosition::AfterCompleted,
109 fallback: None,
110 })
111 .await?;
112 let parked = fork.snapshot().await?;
113 if !matches!(parked.status, RunStatus::Paused)
114 || parked.checkpoint != 0
115 || parked.state.values()["count"] != 40
116 || !parked.outcomes.is_empty()
117 {
118 return Err(
119 AppError::Integration("restore did not create a parked clean copy".into()).into(),
120 );
121 }
122
123 source.delete(completed.checkpoint).await?;
124 if !matches!(source.snapshot().await, Err(AppError::NotFound(_))) {
125 return Err(
126 AppError::Integration("explicit source deletion did not finish".into()).into(),
127 );
128 }
129 // Deletion does not invalidate the copied state. AfterCompleted skips
130 // the first increment, so only the second changes the override: 40 -> 41.
131 let completed = fork
132 .execution(ExecutionOptions::default())
133 .await?
134 .finish()
135 .await?;
136 if !matches!(completed.status, RunStatus::Succeeded)
137 || completed.state.values()["count"] != 41
138 || completed.last_action_ordinal != Some(1)
139 {
140 return Err(
141 AppError::Integration("fork resumed from the wrong boundary".into()).into(),
142 );
143 }
144 println!("Source count: 2; copied count: 40; completed fork count: 41");
145 Ok((
146 workspace.id().clone(),
147 source_selector,
148 RunSelector::Exact {
149 id: fork.id().clone(),
150 occurrence: completed.occurrence,
151 },
152 ))
153 }
154
155 async fn read_reopened(
156 &self,
157 app: &App<StateStore, Agent>,
158 workspace: WorkspaceId,
159 source: RunSelector,
160 fork: RunSelector,
161 ) -> Result<(), AppError> {
162 let workspace = app.workspace(workspace).await?;
163 let workflow = workspace.workflow("history-counter".parse()?).await?;
164 let fork = workflow.open_run(fork).await?;
165 let committed = fork.checkpoint(CheckpointSelector::Action(1)).await?;
166 let lineage = committed
167 .lineage
168 .as_ref()
169 .ok_or_else(|| AppError::Integration("reopened fork lost its source lineage".into()))?;
170 if committed.state.values()["count"] != 41
171 || !matches!(&source, RunSelector::Exact { id, occurrence }
172 if &lineage.source == id && &lineage.source_occurrence == occurrence)
173 || lineage.action_ordinal != Some(0)
174 {
175 return Err(AppError::Integration(
176 "reopened fork history changed".into(),
177 ));
178 }
179 println!(
180 "Reopened fork {} at count 41; source lineage remains readable",
181 fork.id()
182 );
183 Ok(())
184 }23 async fn run() -> Result<(), Box<dyn Error + Send + Sync>> {
24 let definition = WorkflowDefinition::parse(
25 &json!({
26 "schema":"app-kit.workflow/v2", "id":"invoice", "revision":"r1",
27 "state":{"unit_price":7,"total":0}, "required":["unit_price","total"],
28 "reducers":{"unit_price":"reject_concurrent","total":"reject_concurrent"},
29 "entry":"calculate",
30 "nodes":[{"id":"calculate","step":{
31 "kind":"action","action":"invoice","revision":"r1","project_result":false
32 }}], "routes":{}
33 })
34 .to_string(),
35 )?;
36 let store =
37 StateStore::connect(StateStoreConfig::Lance(LanceStateStoreConfig::memory())).await?;
38 // Native caller actions need no Agent value or provider service.
39 let mut app = App::open(
40 AppConfig::<_, Agent>::new(store, Vec::new(), vec![definition])
41 .with_actions(vec![InvoiceAction::new()]),
42 )
43 .await?;
44 let observed = tokio::time::timeout(Duration::from_secs(15), async {
45 let workspace = app
46 .create_workspace(WorkspaceConfig {
47 name: Some("invoice example".into()),
48 root: PathBuf::from(env!("CARGO_MANIFEST_DIR")),
49 })
50 .await?;
51 let workflow = workspace.workflow("invoice".parse()?).await?;
52 let mut run = workflow.prepare(WorkflowState::default()).await?;
53 let mut execution = run
54 .execution(ExecutionOptions {
55 inputs: Map::from_iter([
56 ("quantity".into(), json!(6)),
57 ("trace".into(), json!("sample")),
58 ]),
59 ..ExecutionOptions::default()
60 })
61 .await?;
62 let Some(ExecutionEvent::Partial { result, .. }) = execution.next().await? else {
63 return Err(AppError::Integration(
64 "expected the real action partial".into(),
65 ));
66 };
67 if result.get("progress") != Some(&json!("calculated")) {
68 return Err(AppError::Integration("partial changed".into()));
69 }
70 let Some(ExecutionEvent::Committed { outcomes, .. }) = execution.step().await? else {
71 return Err(AppError::Integration(
72 "expected one committed boundary".into(),
73 ));
74 };
75 let expected =
76 json!({"quantity":6,"label":"order","currency":"USD","trace":"sample","total":42});
77 if outcomes.len() != 1 || Value::Object(outcomes[0].result.clone()) != expected {
78 return Err(AppError::Integration(
79 "resolved inputs or action result changed".into(),
80 ));
81 }
82 let terminal = execution.finish().await?;
83 if !matches!(terminal.status, RunStatus::Succeeded)
84 || terminal.state.values()
85 != json!({"unit_price":7,"total":42}).as_object().unwrap()
86 {
87 return Err(AppError::Integration(
88 "only the explicit total write should persist".into(),
89 ));
90 }
91 println!(
92 "Partial: calculated; committed total: 42; transient inputs stayed out of state"
93 );
94 Ok::<(), AppError>(())
95 })
96 .await;
97 let closed = tokio::time::timeout(
98 Duration::from_secs(15),
99 app.shutdown(Shutdown::CancelAndDrain {
100 timeout: Duration::from_secs(10),
101 }),
102 )
103 .await;
104 drop(app);
105 observed.map_err(|_| AppError::Timeout("invoice execution timed out".into()))??;
106 closed.map_err(|_| AppError::Timeout("invoice cleanup timed out".into()))??;
107 Ok(())
108 }Sourcepub async fn delete(&mut self, expected_revision: u64) -> Result<(), AppError>
pub async fn delete(&mut self, expected_revision: u64) -> Result<(), AppError>
Deletes this workspace after proving its revision fence.
Seals new admission, requests cancellation, and deletes lower workspaces only after every run is terminal with no unresolved effect. Progress is durable and resumable; terminal run history is retained until explicit run deletion.
§Errors
Returns Conflict for a stale revision or incompatible deletion intent, Busy
while unresolved work prevents deletion, and Closed for revoked ownership.
Invalid fences, storage/lower failures, or expired observation return
InvalidInput, Integration, or Timeout.
§Example
let revision = workspace.snapshot().await?.revision;
workspace.delete(revision).await?;
assert!(app.list_workspaces(None, std::num::NonZeroUsize::new(10).unwrap()).await?.is_empty());Auto Trait Implementations§
impl<CS, A, X = NoActions, C = NoConditions, T = JsonState, H = NoHooks, E = InlineExecutor> !RefUnwindSafe for Workspace<CS, A, X, C, T, H, E>
impl<CS, A, X = NoActions, C = NoConditions, T = JsonState, H = NoHooks, E = InlineExecutor> !UnwindSafe for Workspace<CS, A, X, C, T, H, E>
impl<CS, A, X, C, T, H, E> Freeze for Workspace<CS, A, X, C, T, H, E>where
WorkspaceRuntime<CS, A, X, C, T, H, E>: Freeze,
impl<CS, A, X, C, T, H, E> Send for Workspace<CS, A, X, C, T, H, E>where
WorkspaceRuntime<CS, A, X, C, T, H, E>: Send,
impl<CS, A, X, C, T, H, E> Sync for Workspace<CS, A, X, C, T, H, E>where
WorkspaceRuntime<CS, A, X, C, T, H, E>: Sync,
impl<CS, A, X, C, T, H, E> Unpin for Workspace<CS, A, X, C, T, H, E>where
WorkspaceRuntime<CS, A, X, C, T, H, E>: Unpin,
impl<CS, A, X, C, T, H, E> UnsafeUnpin for Workspace<CS, A, X, C, T, H, E>where
WorkspaceRuntime<CS, A, X, C, T, H, E>: UnsafeUnpin,
Blanket Implementations§
Source§impl<T> ArchivePointee for T
impl<T> ArchivePointee for T
Source§type ArchivedMetadata = ()
type ArchivedMetadata = ()
Source§fn pointer_metadata(
_: &<T as ArchivePointee>::ArchivedMetadata,
) -> <T as Pointee>::Metadata
fn pointer_metadata( _: &<T as ArchivePointee>::ArchivedMetadata, ) -> <T as Pointee>::Metadata
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
impl<T> ErasedDestructor for Twhere
T: 'static,
Source§impl<T> FmtForward for T
impl<T> FmtForward for T
Source§fn fmt_binary(self) -> FmtBinary<Self>where
Self: Binary,
fn fmt_binary(self) -> FmtBinary<Self>where
Self: Binary,
self to use its Binary implementation when Debug-formatted.Source§fn fmt_display(self) -> FmtDisplay<Self>where
Self: Display,
fn fmt_display(self) -> FmtDisplay<Self>where
Self: Display,
self to use its Display implementation when
Debug-formatted.Source§fn fmt_lower_exp(self) -> FmtLowerExp<Self>where
Self: LowerExp,
fn fmt_lower_exp(self) -> FmtLowerExp<Self>where
Self: LowerExp,
self to use its LowerExp implementation when
Debug-formatted.Source§fn fmt_lower_hex(self) -> FmtLowerHex<Self>where
Self: LowerHex,
fn fmt_lower_hex(self) -> FmtLowerHex<Self>where
Self: LowerHex,
self to use its LowerHex implementation when
Debug-formatted.Source§fn fmt_octal(self) -> FmtOctal<Self>where
Self: Octal,
fn fmt_octal(self) -> FmtOctal<Self>where
Self: Octal,
self to use its Octal implementation when Debug-formatted.Source§fn fmt_pointer(self) -> FmtPointer<Self>where
Self: Pointer,
fn fmt_pointer(self) -> FmtPointer<Self>where
Self: Pointer,
self to use its Pointer implementation when
Debug-formatted.Source§fn fmt_upper_exp(self) -> FmtUpperExp<Self>where
Self: UpperExp,
fn fmt_upper_exp(self) -> FmtUpperExp<Self>where
Self: UpperExp,
self to use its UpperExp implementation when
Debug-formatted.Source§fn fmt_upper_hex(self) -> FmtUpperHex<Self>where
Self: UpperHex,
fn fmt_upper_hex(self) -> FmtUpperHex<Self>where
Self: UpperHex,
self to use its UpperHex implementation when
Debug-formatted.Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
Source§fn in_current_span(self) -> Instrumented<Self> ⓘ
fn in_current_span(self) -> Instrumented<Self> ⓘ
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§impl<T> IntoRequest<T> for T
impl<T> IntoRequest<T> for T
Source§fn into_request(self) -> Request<T>
fn into_request(self) -> Request<T>
T in a tonic::RequestSource§impl<T> LayoutRaw for T
impl<T> LayoutRaw for T
Source§fn layout_raw(_: <T as Pointee>::Metadata) -> Result<Layout, LayoutError>
fn layout_raw(_: <T as Pointee>::Metadata) -> Result<Layout, LayoutError>
Source§impl<T, N1, N2> Niching<NichedOption<T, N1>> for N2
impl<T, N1, N2> Niching<NichedOption<T, N1>> for N2
Source§unsafe fn is_niched(niched: *const NichedOption<T, N1>) -> bool
unsafe fn is_niched(niched: *const NichedOption<T, N1>) -> bool
Source§fn resolve_niched(out: Place<NichedOption<T, N1>>)
fn resolve_niched(out: Place<NichedOption<T, N1>>)
out indicating that a T is niched.Source§impl<T> Pipe for Twhere
T: ?Sized,
impl<T> Pipe for Twhere
T: ?Sized,
Source§fn pipe<R>(self, func: impl FnOnce(Self) -> R) -> Rwhere
Self: Sized,
fn pipe<R>(self, func: impl FnOnce(Self) -> R) -> Rwhere
Self: Sized,
Source§fn pipe_ref<'a, R>(&'a self, func: impl FnOnce(&'a Self) -> R) -> Rwhere
R: 'a,
fn pipe_ref<'a, R>(&'a self, func: impl FnOnce(&'a Self) -> R) -> Rwhere
R: 'a,
self and passes that borrow into the pipe function. Read moreSource§fn pipe_ref_mut<'a, R>(&'a mut self, func: impl FnOnce(&'a mut Self) -> R) -> Rwhere
R: 'a,
fn pipe_ref_mut<'a, R>(&'a mut self, func: impl FnOnce(&'a mut Self) -> R) -> Rwhere
R: 'a,
self and passes that borrow into the pipe function. Read moreSource§fn pipe_borrow<'a, B, R>(&'a self, func: impl FnOnce(&'a B) -> R) -> R
fn pipe_borrow<'a, B, R>(&'a self, func: impl FnOnce(&'a B) -> R) -> R
Source§fn pipe_borrow_mut<'a, B, R>(
&'a mut self,
func: impl FnOnce(&'a mut B) -> R,
) -> R
fn pipe_borrow_mut<'a, B, R>( &'a mut self, func: impl FnOnce(&'a mut B) -> R, ) -> R
Source§fn pipe_as_ref<'a, U, R>(&'a self, func: impl FnOnce(&'a U) -> R) -> R
fn pipe_as_ref<'a, U, R>(&'a self, func: impl FnOnce(&'a U) -> R) -> R
self, then passes self.as_ref() into the pipe function.Source§fn pipe_as_mut<'a, U, R>(&'a mut self, func: impl FnOnce(&'a mut U) -> R) -> R
fn pipe_as_mut<'a, U, R>(&'a mut self, func: impl FnOnce(&'a mut U) -> R) -> R
self, then passes self.as_mut() into the pipe
function.Source§fn pipe_deref<'a, T, R>(&'a self, func: impl FnOnce(&'a T) -> R) -> R
fn pipe_deref<'a, T, R>(&'a self, func: impl FnOnce(&'a T) -> R) -> R
self, then passes self.deref() into the pipe function.Source§impl<T> Pointable for T
impl<T> Pointable for T
Source§impl<T> PolicyExt for Twhere
T: ?Sized,
impl<T> PolicyExt for Twhere
T: ?Sized,
impl<T> Read<Exclusive, BecauseExclusive> for Twhere
T: ?Sized,
Source§impl<T> Tap for T
impl<T> Tap for T
Source§fn tap_borrow<B>(self, func: impl FnOnce(&B)) -> Self
fn tap_borrow<B>(self, func: impl FnOnce(&B)) -> Self
Borrow<B> of a value. Read moreSource§fn tap_borrow_mut<B>(self, func: impl FnOnce(&mut B)) -> Self
fn tap_borrow_mut<B>(self, func: impl FnOnce(&mut B)) -> Self
BorrowMut<B> of a value. Read moreSource§fn tap_ref<R>(self, func: impl FnOnce(&R)) -> Self
fn tap_ref<R>(self, func: impl FnOnce(&R)) -> Self
AsRef<R> view of a value. Read moreSource§fn tap_ref_mut<R>(self, func: impl FnOnce(&mut R)) -> Self
fn tap_ref_mut<R>(self, func: impl FnOnce(&mut R)) -> Self
AsMut<R> view of a value. Read moreSource§fn tap_deref<T>(self, func: impl FnOnce(&T)) -> Self
fn tap_deref<T>(self, func: impl FnOnce(&T)) -> Self
Deref::Target of a value. Read moreSource§fn tap_deref_mut<T>(self, func: impl FnOnce(&mut T)) -> Self
fn tap_deref_mut<T>(self, func: impl FnOnce(&mut T)) -> Self
Deref::Target of a value. Read moreSource§fn tap_dbg(self, func: impl FnOnce(&Self)) -> Self
fn tap_dbg(self, func: impl FnOnce(&Self)) -> Self
.tap() only in debug builds, and is erased in release builds.Source§fn tap_mut_dbg(self, func: impl FnOnce(&mut Self)) -> Self
fn tap_mut_dbg(self, func: impl FnOnce(&mut Self)) -> Self
.tap_mut() only in debug builds, and is erased in release
builds.Source§fn tap_borrow_dbg<B>(self, func: impl FnOnce(&B)) -> Self
fn tap_borrow_dbg<B>(self, func: impl FnOnce(&B)) -> Self
.tap_borrow() only in debug builds, and is erased in release
builds.Source§fn tap_borrow_mut_dbg<B>(self, func: impl FnOnce(&mut B)) -> Self
fn tap_borrow_mut_dbg<B>(self, func: impl FnOnce(&mut B)) -> Self
.tap_borrow_mut() only in debug builds, and is erased in release
builds.Source§fn tap_ref_dbg<R>(self, func: impl FnOnce(&R)) -> Self
fn tap_ref_dbg<R>(self, func: impl FnOnce(&R)) -> Self
.tap_ref() only in debug builds, and is erased in release
builds.Source§fn tap_ref_mut_dbg<R>(self, func: impl FnOnce(&mut R)) -> Self
fn tap_ref_mut_dbg<R>(self, func: impl FnOnce(&mut R)) -> Self
.tap_ref_mut() only in debug builds, and is erased in release
builds.Source§fn tap_deref_dbg<T>(self, func: impl FnOnce(&T)) -> Self
fn tap_deref_dbg<T>(self, func: impl FnOnce(&T)) -> Self
.tap_deref() only in debug builds, and is erased in release
builds.