Skip to main content

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}