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, capture_input};
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    Fmt,
25    Build,
26    Vcs,
27}
28
29impl Component {
30    /// Keep executable discovery independent of localized help or command aliases.
31    #[must_use]
32    pub const fn name(self) -> &'static str {
33        match self {
34            Self::Project => "eska-project",
35            Self::Check => "eska-check",
36            Self::Fmt => "eska-fmt",
37            Self::Build => "eska-build",
38            Self::Vcs => "eska-vcs",
39        }
40    }
41
42    /// Explicit paths always take precedence and never silently fall back after failure.
43    #[must_use]
44    pub const fn variable(self) -> &'static str {
45        match self {
46            Self::Project => "ESKA_PROJECT_BINARY",
47            Self::Check => "ESKA_CHECK_BINARY",
48            Self::Fmt => "ESKA_FMT_BINARY",
49            Self::Build => "ESKA_BUILD_BINARY",
50            Self::Vcs => "ESKA_VCS_BINARY",
51        }
52    }
53}
54
55/// Structured component failures never imply that a repository or source is clean.
56#[derive(Debug)]
57pub enum Error {
58    Missing {
59        component: Component,
60    },
61    Invalid {
62        component: Component,
63        path: PathBuf,
64        source: io::Error,
65    },
66    Selection {
67        component: Component,
68        path: PathBuf,
69    },
70    Incompatible {
71        tool: String,
72        receipt: PathBuf,
73    },
74    Signal(io::Error),
75    Process {
76        component: Component,
77        source: io::Error,
78    },
79    Timeout,
80    Cancelled,
81    InvalidResponse,
82    OutputTooLarge,
83    Failed,
84    Rejected {
85        component: Component,
86        code: String,
87        detail: Option<String>,
88    },
89}
90
91impl Error {
92    /// Stable internal categories can be localized by each consuming CLI.
93    #[must_use]
94    pub const fn code(&self) -> &'static str {
95        match self {
96            Self::Missing { .. } => "component_missing",
97            Self::Invalid { .. } | Self::Selection { .. } => "component_invalid",
98            Self::Incompatible { .. } => "component_incompatible",
99            Self::Signal(_) => "signal_handler",
100            Self::Process { .. } => "component_process",
101            Self::Timeout => "component_timeout",
102            Self::Cancelled => "cancelled",
103            Self::InvalidResponse => "component_response_invalid",
104            Self::OutputTooLarge => "component_output_too_large",
105            Self::Failed => "component_failed",
106            Self::Rejected { .. } => "component_rejected",
107        }
108    }
109}
110
111#[cfg(all(test, unix))]
112#[path = "../../../tests/support/mod.rs"]
113mod test_support;