Skip to main content

repolith_core/
action.rs

1use crate::types::{ActionId, BuildError, BuildOutput, Ctx, Sha256};
2use async_trait::async_trait;
3
4/// Represents an executable action within the repolith graph.
5///
6/// Actions are the fundamental units of work. They declare their dependencies,
7/// compute their input state, and execute their logic.
8///
9/// Note: Long-running `execute` implementations should periodically poll
10/// `ctx.cancel.is_cancelled()` to support graceful termination.
11#[async_trait]
12pub trait Action: Send + Sync {
13    /// Returns the unique identifier for this action.
14    fn id(&self) -> ActionId;
15
16    /// Returns the list of action IDs that this action depends on.
17    fn deps(&self) -> Vec<ActionId>;
18
19    /// Computes the hash representing the input state of this action.
20    /// This is used for caching and determining if the action needs to run.
21    async fn input_hash(&self, ctx: &Ctx) -> Result<Sha256, BuildError>;
22
23    /// Executes the action's core logic.
24    async fn execute(&self, ctx: &Ctx) -> Result<BuildOutput, BuildError>;
25}
26
27#[cfg(test)]
28mod tests {
29    use super::*;
30
31    struct MockAction;
32
33    #[async_trait]
34    impl Action for MockAction {
35        fn id(&self) -> ActionId {
36            ActionId("mock".to_string())
37        }
38
39        fn deps(&self) -> Vec<ActionId> {
40            vec![]
41        }
42
43        async fn input_hash(&self, _ctx: &Ctx) -> Result<Sha256, BuildError> {
44            Ok(Sha256([0; 32]))
45        }
46
47        async fn execute(&self, _ctx: &Ctx) -> Result<BuildOutput, BuildError> {
48            Ok(BuildOutput {
49                output_hash: Sha256([1; 32]),
50                stdout: String::new(),
51            })
52        }
53    }
54
55    #[test]
56    fn test_action_is_object_safe() {
57        // This line will fail to compile if `Action` is not object-safe.
58        let _action: Box<dyn Action> = Box::new(MockAction);
59    }
60}