Skip to main content

eska_tool_runtime/
lib.rs

1//! Lightweight component discovery and owned process I/O; no domain engines or localization.
2
3mod capture;
4pub mod contract;
5mod exchange;
6pub mod installation;
7mod process;
8mod query;
9mod resolver;
10pub mod vcs;
11
12pub use capture::capture;
13pub use exchange::{Operation, exchange};
14pub use process::delegate;
15pub use query::{QueryPolicy, query, query_with_policy};
16pub use resolver::{resolve, resolve_at};
17
18use std::{io, path::PathBuf};
19
20#[derive(Clone, Copy, Debug)]
21pub enum Component {
22    Project,
23    Check,
24    Build,
25    Vcs,
26}
27
28impl Component {
29    /// Keep executable discovery independent of localized help or command aliases.
30    #[must_use]
31    pub const fn name(self) -> &'static str {
32        match self {
33            Self::Project => "eska-project",
34            Self::Check => "eska-check",
35            Self::Build => "eska-build",
36            Self::Vcs => "eska-vcs",
37        }
38    }
39
40    /// Explicit paths always take precedence and never silently fall back after failure.
41    #[must_use]
42    pub const fn variable(self) -> &'static str {
43        match self {
44            Self::Project => "ESKA_PROJECT_BINARY",
45            Self::Check => "ESKA_CHECK_BINARY",
46            Self::Build => "ESKA_BUILD_BINARY",
47            Self::Vcs => "ESKA_VCS_BINARY",
48        }
49    }
50}
51
52/// Structured component failures never imply that a repository or source is clean.
53#[derive(Debug)]
54pub enum Error {
55    Missing {
56        component: Component,
57    },
58    Invalid {
59        component: Component,
60        path: PathBuf,
61        source: io::Error,
62    },
63    Selection {
64        component: Component,
65        path: PathBuf,
66    },
67    Incompatible {
68        tool: String,
69        receipt: PathBuf,
70    },
71    Signal(io::Error),
72    Process {
73        component: Component,
74        source: io::Error,
75    },
76    Timeout,
77    Cancelled,
78    InvalidResponse,
79    OutputTooLarge,
80    Failed,
81    Rejected {
82        component: Component,
83        code: String,
84        detail: Option<String>,
85    },
86}
87
88impl Error {
89    /// Stable internal categories can be localized by each consuming CLI.
90    #[must_use]
91    pub const fn code(&self) -> &'static str {
92        match self {
93            Self::Missing { .. } => "component_missing",
94            Self::Invalid { .. } | Self::Selection { .. } => "component_invalid",
95            Self::Incompatible { .. } => "component_incompatible",
96            Self::Signal(_) => "signal_handler",
97            Self::Process { .. } => "component_process",
98            Self::Timeout => "component_timeout",
99            Self::Cancelled => "cancelled",
100            Self::InvalidResponse => "component_response_invalid",
101            Self::OutputTooLarge => "component_output_too_large",
102            Self::Failed => "component_failed",
103            Self::Rejected { .. } => "component_rejected",
104        }
105    }
106}
107
108#[cfg(test)]
109#[path = "../../../tests/support/mod.rs"]
110mod test_support;