1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// Copyright by contributors to this project.
// SPDX-License-Identifier: (Apache-2.0 OR MIT)
//! Error types for openjd-sessions.
use std::path::PathBuf;
use crate::session::SessionState;
/// Errors that can occur during session operations.
///
/// ```
/// use openjd_sessions::SessionError;
///
/// let err = SessionError::Runtime("something went wrong".into());
/// assert_eq!(err.to_string(), "something went wrong");
/// ```
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum SessionError {
/// Session is not in the expected state for the requested operation.
#[error("Session must be in {} state, current: {current}", format_expected(.expected))]
InvalidState {
expected: Vec<SessionState>,
current: SessionState,
},
/// An environment's onEnter or onExit script failed.
#[error("Environment '{name}' {action} failed: {reason}")]
EnvironmentScriptFailed {
name: String,
action: String,
reason: String,
},
/// Failed to resolve a format string expression.
#[error("Failed to resolve {context}: {reason}")]
FormatString { context: String, reason: String },
/// Failed to write an embedded file.
#[error("Failed to write embedded file '{name}': {source}")]
EmbeddedFile {
name: String,
#[source]
source: std::io::Error,
},
/// An embedded file's filename is not a safe single path component.
///
/// Raised when a `filename` field's value contains path separators,
/// parent-directory components, null bytes, or is otherwise unsafe.
/// This is a defense-in-depth check; `openjd-model` also rejects
/// path separators in filenames at template validation time per the
/// 2023-09 spec (§6.1.1 `<Filename>`).
#[error("Embedded file '{name}' has unsafe filename '{filename}': {reason}")]
EmbeddedFilePath {
name: String,
filename: String,
reason: String,
},
/// Failed to create or access the working directory.
#[error("Failed to create working directory {path}: {source}")]
WorkingDirectory {
path: PathBuf,
#[source]
source: std::io::Error,
},
/// Subprocess failed to start.
#[error("Failed to start subprocess '{command}': {source}")]
SubprocessStart {
command: String,
#[source]
source: std::io::Error,
},
/// Failed to create or manage a temp directory.
#[error("Failed to create temp directory in {path}: {source}")]
TempDir {
path: PathBuf,
#[source]
source: std::io::Error,
},
/// A generic runtime error.
#[error("{0}")]
Runtime(String),
/// Attempted to enter an environment that is already active in this session.
#[error("Environment {id} has already been entered in this Session.")]
DuplicateEnvironment { id: String },
/// Referenced an environment identifier that does not exist in this session.
#[error("Unknown environment identifier: {identifier}")]
UnknownEnvironment { identifier: String },
/// Failed to set file ownership or permissions (chown/chmod).
#[error("Failed to set permissions on '{path}': {reason}")]
PathPermissions { path: String, reason: String },
/// Cross-user helper IPC communication failure.
#[error("Cross-user helper error: {0}")]
HelperCommunication(String),
/// Attempted to exit an environment out of LIFO order.
#[error(
"Must exit the most recently entered environment first. Expected {expected}, got {got}"
)]
LifoViolation { expected: String, got: String },
}
fn format_expected(states: &[SessionState]) -> String {
match states {
[single] => single.to_string(),
_ => states
.iter()
.map(|s| s.to_string())
.collect::<Vec<_>>()
.join(" or "),
}
}