lean_ctx/core/context_bundle/export.rs
1// SPDX-License-Identifier: Apache-2.0
2//! Where a bundle may be written as a file.
3//!
4//! A bundle file leaves the delivery path of a tool result, so its
5//! destination follows the rule the package export already applies
6//! (`cli::pack_cmd::checkpoint`): bound to the project the bundle was built
7//! for, also when the general tool jail is compiled out or switched off.
8//! The caller writes the RELEASED text there, never the text as built.
9
10use std::path::{Path, PathBuf};
11
12/// The file a bundle of the project `root` may be written to, or the reason
13/// it may not. `candidate` is the absolute destination the caller named.
14pub fn destination(tool: &str, root: &Path, candidate: &Path) -> Result<PathBuf, String> {
15 let (output, _) = crate::core::io_boundary::jail_and_check_export_path(tool, candidate, root)?;
16 // The jail also admits lean-ctx's own data and state directories and
17 // configured allow paths; a bundle of project sources belongs inside the
18 // project only. Both sides go through the same canonicalizer.
19 let canonical_root = crate::core::pathutil::canonicalize_secure(root)
20 .map_err(|_| "project root is unavailable".to_string())?;
21 let inside = output
22 .parent()
23 .and_then(|parent| crate::core::pathutil::canonicalize_secure(parent).ok())
24 .is_some_and(|parent| parent.starts_with(&canonical_root));
25 if !inside {
26 return Err("a bundle file must stay inside the project root".to_string());
27 }
28 crate::core::pathjail::enforce_writable(&output)?;
29 Ok(output)
30}