Files
galaxy/app/src/terminal/writeable_pty/bootstrap_file/windows.rs
T

123 lines
4.0 KiB
Rust

#[cfg(feature = "local_fs")]
use std::io::Write;
use std::path::PathBuf;
use typed_path::TypedPath;
use warp_util::path::{convert_wsl_to_windows_host_path, WSLPathConversionError};
use crate::terminal::shell::ShellType;
/// Represents a temporary file used for bootstrap.
/// This is necessary because [`tempfile`] requires us to hold on to the file handle. However,
/// this causes Windows to prevent the shell from reading the file (since it is being used by
/// another process).
#[derive(Debug)]
pub struct TempBootstrapFile {
file_path: FilePath,
}
#[derive(Debug)]
enum FilePath {
Direct(PathBuf),
Wsl(PathBuf),
}
#[derive(thiserror::Error, Debug)]
pub enum TempBootstrapFileError {
#[error(transparent)]
ConvertPath(#[from] WSLPathConversionError),
#[error("could not create temporary file")]
Create(std::io::Error),
#[error("could not write bootstrap script to temporary file")]
Write(std::io::Error),
#[error("could not flush to temporary file")]
Flush(std::io::Error),
#[error("could not persist temporary file")]
Persist(#[from] tempfile::PersistError),
}
impl TempBootstrapFile {
pub fn new<C, S>(
builder: tempfile::Builder,
contents: C,
wsl_distribution: Option<S>,
) -> Result<Self, TempBootstrapFileError>
where
C: AsRef<[u8]>,
S: AsRef<str>,
{
let mut file = match &wsl_distribution {
Some(distro_name) => {
let wsl_temp_dir = convert_wsl_to_windows_host_path(
&TypedPath::from("/tmp"),
distro_name.as_ref(),
)?;
builder
.tempfile_in(wsl_temp_dir)
.map_err(TempBootstrapFileError::Create)?
}
None => builder.tempfile().map_err(TempBootstrapFileError::Create)?,
};
file.write_all(contents.as_ref())
.map_err(TempBootstrapFileError::Write)?;
file.flush().map_err(TempBootstrapFileError::Flush)?;
// We persist the file here because there are issues with using NamedTempFile
// as-is. If we hold on to the file handle without persisting, Windows
// does not let the shell access the file.
file.keep()
.map(|(_file, path)| Self {
file_path: match wsl_distribution.is_some() {
true => FilePath::Wsl(path),
false => FilePath::Direct(path),
},
})
.map_err(TempBootstrapFileError::from)
}
pub fn path_as_bytes(&self) -> Option<Vec<u8>> {
match &self.file_path {
FilePath::Direct(path) => path
.as_os_str()
.to_str()
.map(|path_str| path_str.as_bytes().to_vec()),
FilePath::Wsl(path) => {
let file_name = path
.file_name()
.map(|file_name| file_name.to_string_lossy())?;
// We need to source the path from the Linux distribution's perspective here.
let path = format!("/tmp/{file_name}");
Some(path.as_bytes().to_vec())
}
}
}
}
impl Drop for TempBootstrapFile {
fn drop(&mut self) {
if let Err(err) = match &self.file_path {
FilePath::Direct(path) => std::fs::remove_file(path),
FilePath::Wsl(path) => std::fs::remove_file(path),
} {
log::warn!("Unable to remove temporary bootstrap file: {err:?}");
}
}
}
/// Returns the path to the permanent bootstrap file in bytes, if it exists.
///
/// Currently we only create a permanent bootstrap file for PowerShell, located
/// alongside the Warp executable.
pub fn path_to_permanent_bootstrap_file(shell_type: ShellType) -> Option<Vec<u8>> {
if shell_type != ShellType::PowerShell {
return None;
}
let install_dir = crate::util::windows::install_dir().ok()?;
let path = install_dir.join("pwsh.ps1");
path.is_file()
.then(|| path.as_os_str().to_str().map(|s| s.as_bytes().to_vec()))
.flatten()
}