mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
Use AbsolutePathBuf for cwd state (#15710)
Migrate `cwd` and related session/config state to `AbsolutePathBuf` so downstream consumers consistently see absolute working directories. Add test-only `.abs()` helpers for `Path`, `PathBuf`, and `TempDir`, and update branch-local tests to use them instead of `AbsolutePathBuf::try_from(...)`. For the remaining TUI/app-server snapshot coverage that renders absolute cwd values, keep the snapshots unchanged and skip the Windows-only cases where the platform-specific absolute path layout differs.
This commit is contained in:
committed by
GitHub
Unverified
parent
178c3b15b4
commit
504aeb0e09
@@ -42,6 +42,8 @@ use crate::read_session_model;
|
||||
use crate::render::highlight::highlight_bash_to_lines;
|
||||
use crate::render::renderable::Renderable;
|
||||
use crate::resume_picker::SessionSelection;
|
||||
#[cfg(test)]
|
||||
use crate::test_support::PathBufExt;
|
||||
use crate::tui;
|
||||
use crate::tui::TuiEvent;
|
||||
use crate::update_action::UpdateAction;
|
||||
@@ -1039,7 +1041,7 @@ impl App {
|
||||
|
||||
async fn refresh_in_memory_config_from_disk(&mut self) -> Result<()> {
|
||||
let mut config = self
|
||||
.rebuild_config_for_cwd(self.chat_widget.config_ref().cwd.clone())
|
||||
.rebuild_config_for_cwd(self.chat_widget.config_ref().cwd.to_path_buf())
|
||||
.await?;
|
||||
self.apply_runtime_policy_overrides(&mut config);
|
||||
self.config = config;
|
||||
@@ -1406,7 +1408,7 @@ impl App {
|
||||
self.chat_widget.current_model(),
|
||||
self.chat_widget.current_service_tier(),
|
||||
),
|
||||
self.config.cwd.clone(),
|
||||
self.config.cwd.to_path_buf(),
|
||||
version,
|
||||
)
|
||||
.display_lines(width)
|
||||
@@ -1702,7 +1704,7 @@ impl App {
|
||||
cwd: self
|
||||
.thread_cwd(thread_id)
|
||||
.await
|
||||
.unwrap_or_else(|| self.config.cwd.clone()),
|
||||
.unwrap_or_else(|| self.config.cwd.to_path_buf()),
|
||||
changes: HashMap::new(),
|
||||
}),
|
||||
),
|
||||
@@ -3077,7 +3079,7 @@ impl App {
|
||||
chat_widget
|
||||
.maybe_prompt_windows_sandbox_enable(should_prompt_windows_sandbox_nux_at_startup);
|
||||
|
||||
let file_search = FileSearchManager::new(config.cwd.clone(), app_event_tx.clone());
|
||||
let file_search = FileSearchManager::new(config.cwd.to_path_buf(), app_event_tx.clone());
|
||||
#[cfg(not(debug_assertions))]
|
||||
let upgrade_version = crate::updates::get_upgrade_version(&config);
|
||||
|
||||
@@ -3144,7 +3146,13 @@ impl App {
|
||||
let tx = app.app_event_tx.clone();
|
||||
let logs_base_dir = app.config.codex_home.clone();
|
||||
let sandbox_policy = app.config.permissions.sandbox_policy.get().clone();
|
||||
Self::spawn_world_writable_scan(cwd, env_map, logs_base_dir, sandbox_policy, tx);
|
||||
Self::spawn_world_writable_scan(
|
||||
cwd.to_path_buf(),
|
||||
env_map,
|
||||
logs_base_dir,
|
||||
sandbox_policy,
|
||||
tx,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3369,7 +3377,7 @@ impl App {
|
||||
.await?
|
||||
{
|
||||
SessionSelection::Resume(target_session) => {
|
||||
let current_cwd = self.config.cwd.clone();
|
||||
let current_cwd = self.config.cwd.to_path_buf();
|
||||
let resume_cwd = if self.remote_app_server_url.is_some() {
|
||||
current_cwd.clone()
|
||||
} else {
|
||||
@@ -3417,7 +3425,8 @@ impl App {
|
||||
self.shutdown_current_thread(app_server).await;
|
||||
self.config = resume_config;
|
||||
tui.set_notification_method(self.config.tui_notification_method);
|
||||
self.file_search.update_search_dir(self.config.cwd.clone());
|
||||
self.file_search
|
||||
.update_search_dir(self.config.cwd.to_path_buf());
|
||||
match self
|
||||
.replace_chat_widget_with_app_server_thread(tui, resumed)
|
||||
.await
|
||||
@@ -3715,7 +3724,8 @@ impl App {
|
||||
plugin_display_name,
|
||||
result,
|
||||
);
|
||||
if install_succeeded && self.chat_widget.config_ref().cwd == cwd {
|
||||
if install_succeeded && self.chat_widget.config_ref().cwd.as_path() == cwd.as_path()
|
||||
{
|
||||
self.fetch_plugins_list(app_server, cwd.clone());
|
||||
if should_refresh_plugin_detail {
|
||||
self.fetch_plugin_detail(
|
||||
@@ -4190,7 +4200,9 @@ impl App {
|
||||
plugin_display_name,
|
||||
result,
|
||||
);
|
||||
if uninstall_succeeded && self.chat_widget.config_ref().cwd == cwd {
|
||||
if uninstall_succeeded
|
||||
&& self.chat_widget.config_ref().cwd.as_path() == cwd.as_path()
|
||||
{
|
||||
self.fetch_plugins_list(app_server, cwd);
|
||||
}
|
||||
}
|
||||
@@ -4377,7 +4389,7 @@ impl App {
|
||||
let logs_base_dir = self.config.codex_home.clone();
|
||||
let sandbox_policy = self.config.permissions.sandbox_policy.get().clone();
|
||||
Self::spawn_world_writable_scan(
|
||||
cwd,
|
||||
cwd.to_path_buf(),
|
||||
env_map,
|
||||
logs_base_dir,
|
||||
sandbox_policy,
|
||||
@@ -6693,7 +6705,7 @@ mod tests {
|
||||
let (mut app, mut app_event_rx, mut op_rx) = make_test_app_with_channels().await;
|
||||
let codex_home = tempdir()?;
|
||||
app.config.codex_home = codex_home.path().to_path_buf();
|
||||
let config_toml_path = AbsolutePathBuf::try_from(codex_home.path().join("config.toml"))?;
|
||||
let config_toml_path = codex_home.path().join("config.toml").abs();
|
||||
let config_toml = "approvals_reviewer = \"guardian_subagent\"\napproval_policy = \"on-request\"\nsandbox_mode = \"workspace-write\"\n\n[features]\nguardian_approval = true\n";
|
||||
std::fs::write(config_toml_path.as_path(), config_toml)?;
|
||||
let user_config = toml::from_str::<TomlValue>(config_toml)?;
|
||||
@@ -6785,7 +6797,7 @@ mod tests {
|
||||
let codex_home = tempdir()?;
|
||||
app.config.codex_home = codex_home.path().to_path_buf();
|
||||
let guardian_approvals = guardian_approvals_mode();
|
||||
let config_toml_path = AbsolutePathBuf::try_from(codex_home.path().join("config.toml"))?;
|
||||
let config_toml_path = codex_home.path().join("config.toml").abs();
|
||||
let config_toml = "approvals_reviewer = \"user\"\n";
|
||||
std::fs::write(config_toml_path.as_path(), config_toml)?;
|
||||
let user_config = toml::from_str::<TomlValue>(config_toml)?;
|
||||
@@ -6852,7 +6864,7 @@ mod tests {
|
||||
let (mut app, mut app_event_rx, mut op_rx) = make_test_app_with_channels().await;
|
||||
let codex_home = tempdir()?;
|
||||
app.config.codex_home = codex_home.path().to_path_buf();
|
||||
let config_toml_path = AbsolutePathBuf::try_from(codex_home.path().join("config.toml"))?;
|
||||
let config_toml_path = codex_home.path().join("config.toml").abs();
|
||||
let config_toml = "approvals_reviewer = \"user\"\napproval_policy = \"on-request\"\nsandbox_mode = \"workspace-write\"\n\n[features]\nguardian_approval = true\n";
|
||||
std::fs::write(config_toml_path.as_path(), config_toml)?;
|
||||
let user_config = toml::from_str::<TomlValue>(config_toml)?;
|
||||
@@ -6913,7 +6925,7 @@ mod tests {
|
||||
app.config.codex_home = codex_home.path().to_path_buf();
|
||||
let guardian_approvals = guardian_approvals_mode();
|
||||
app.active_profile = Some("guardian".to_string());
|
||||
let config_toml_path = AbsolutePathBuf::try_from(codex_home.path().join("config.toml"))?;
|
||||
let config_toml_path = codex_home.path().join("config.toml").abs();
|
||||
let config_toml = "profile = \"guardian\"\napprovals_reviewer = \"user\"\n";
|
||||
std::fs::write(config_toml_path.as_path(), config_toml)?;
|
||||
let user_config = toml::from_str::<TomlValue>(config_toml)?;
|
||||
@@ -6983,7 +6995,7 @@ mod tests {
|
||||
let codex_home = tempdir()?;
|
||||
app.config.codex_home = codex_home.path().to_path_buf();
|
||||
app.active_profile = Some("guardian".to_string());
|
||||
let config_toml_path = AbsolutePathBuf::try_from(codex_home.path().join("config.toml"))?;
|
||||
let config_toml_path = codex_home.path().join("config.toml").abs();
|
||||
let config_toml = r#"
|
||||
profile = "guardian"
|
||||
approvals_reviewer = "user"
|
||||
@@ -7071,7 +7083,7 @@ guardian_approval = true
|
||||
let codex_home = tempdir()?;
|
||||
app.config.codex_home = codex_home.path().to_path_buf();
|
||||
app.active_profile = Some("guardian".to_string());
|
||||
let config_toml_path = AbsolutePathBuf::try_from(codex_home.path().join("config.toml"))?;
|
||||
let config_toml_path = codex_home.path().join("config.toml").abs();
|
||||
let config_toml = "profile = \"guardian\"\napprovals_reviewer = \"guardian_subagent\"\n\n[features]\nguardian_approval = true\n";
|
||||
std::fs::write(config_toml_path.as_path(), config_toml)?;
|
||||
let user_config = toml::from_str::<TomlValue>(config_toml)?;
|
||||
@@ -7652,7 +7664,7 @@ guardian_approval = true
|
||||
|
||||
async fn render_clear_ui_header_after_long_transcript_for_snapshot() -> String {
|
||||
let mut app = make_test_app().await;
|
||||
app.config.cwd = PathBuf::from("/tmp/project");
|
||||
app.config.cwd = PathBuf::from("/tmp/project").abs();
|
||||
app.chat_widget.set_model("gpt-test");
|
||||
app.chat_widget
|
||||
.set_reasoning_effort(Some(ReasoningEffortConfig::High));
|
||||
@@ -7705,7 +7717,7 @@ guardian_approval = true
|
||||
approval_policy: AskForApproval::Never,
|
||||
approvals_reviewer: ApprovalsReviewer::User,
|
||||
sandbox_policy: SandboxPolicy::new_read_only_policy(),
|
||||
cwd: PathBuf::from("/tmp/project"),
|
||||
cwd: PathBuf::from("/tmp/project").abs().to_path_buf(),
|
||||
reasoning_effort: Some(ReasoningEffortConfig::High),
|
||||
history_log_id: 0,
|
||||
history_entry_count: 0,
|
||||
@@ -7763,21 +7775,33 @@ guardian_approval = true
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
#[cfg_attr(
|
||||
target_os = "windows",
|
||||
ignore = "snapshot path rendering differs on Windows"
|
||||
)]
|
||||
async fn clear_ui_after_long_transcript_snapshots_fresh_header_only() {
|
||||
let rendered = render_clear_ui_header_after_long_transcript_for_snapshot().await;
|
||||
assert_snapshot!("clear_ui_after_long_transcript_fresh_header_only", rendered);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
#[cfg_attr(
|
||||
target_os = "windows",
|
||||
ignore = "snapshot path rendering differs on Windows"
|
||||
)]
|
||||
async fn ctrl_l_clear_ui_after_long_transcript_reuses_clear_header_snapshot() {
|
||||
let rendered = render_clear_ui_header_after_long_transcript_for_snapshot().await;
|
||||
assert_snapshot!("clear_ui_after_long_transcript_fresh_header_only", rendered);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
#[cfg_attr(
|
||||
target_os = "windows",
|
||||
ignore = "snapshot path rendering differs on Windows"
|
||||
)]
|
||||
async fn clear_ui_header_shows_fast_status_only_for_gpt54() {
|
||||
let mut app = make_test_app().await;
|
||||
app.config.cwd = PathBuf::from("/tmp/project");
|
||||
app.config.cwd = PathBuf::from("/tmp/project").abs();
|
||||
app.chat_widget.set_model("gpt-5.4");
|
||||
app.chat_widget
|
||||
.set_reasoning_effort(Some(ReasoningEffortConfig::XHigh));
|
||||
@@ -7803,7 +7827,7 @@ guardian_approval = true
|
||||
async fn make_test_app() -> App {
|
||||
let (chat_widget, app_event_tx, _rx, _op_rx) = make_chatwidget_manual_with_sender().await;
|
||||
let config = chat_widget.config_ref().clone();
|
||||
let file_search = FileSearchManager::new(config.cwd.clone(), app_event_tx.clone());
|
||||
let file_search = FileSearchManager::new(config.cwd.to_path_buf(), app_event_tx.clone());
|
||||
let model = codex_core::test_support::get_model_offline(config.model.as_deref());
|
||||
let session_telemetry = test_session_telemetry(&config, model.as_str());
|
||||
|
||||
@@ -7853,7 +7877,7 @@ guardian_approval = true
|
||||
) {
|
||||
let (chat_widget, app_event_tx, rx, op_rx) = make_chatwidget_manual_with_sender().await;
|
||||
let config = chat_widget.config_ref().clone();
|
||||
let file_search = FileSearchManager::new(config.cwd.clone(), app_event_tx.clone());
|
||||
let file_search = FileSearchManager::new(config.cwd.to_path_buf(), app_event_tx.clone());
|
||||
let model = codex_core::test_support::get_model_offline(config.model.as_deref());
|
||||
let session_telemetry = test_session_telemetry(&config, model.as_str());
|
||||
|
||||
@@ -8598,7 +8622,7 @@ guardian_approval = true
|
||||
}),
|
||||
});
|
||||
|
||||
assert_eq!(app.chat_widget.config_ref().cwd, next_cwd);
|
||||
assert_eq!(app.chat_widget.config_ref().cwd.to_path_buf(), next_cwd);
|
||||
assert_eq!(app.config.cwd, original_cwd);
|
||||
|
||||
app.refresh_in_memory_config_from_disk().await?;
|
||||
@@ -8618,7 +8642,7 @@ guardian_approval = true
|
||||
let current_cwd = current_config.cwd.clone();
|
||||
|
||||
let resume_config = app
|
||||
.rebuild_config_for_resume_or_fallback(¤t_cwd, current_cwd.clone())
|
||||
.rebuild_config_for_resume_or_fallback(¤t_cwd, current_cwd.to_path_buf())
|
||||
.await?;
|
||||
|
||||
assert_eq!(resume_config, current_config);
|
||||
|
||||
@@ -207,6 +207,7 @@ use codex_terminal_detection::Multiplexer;
|
||||
use codex_terminal_detection::TerminalInfo;
|
||||
use codex_terminal_detection::TerminalName;
|
||||
use codex_terminal_detection::terminal_info;
|
||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||
use codex_utils_sleep_inhibitor::SleepInhibitor;
|
||||
use crossterm::event::KeyCode;
|
||||
use crossterm::event::KeyEvent;
|
||||
@@ -1778,7 +1779,12 @@ impl ChatWidget {
|
||||
self.forked_from = event.forked_from_id;
|
||||
self.current_rollout_path = event.rollout_path.clone();
|
||||
self.current_cwd = Some(event.cwd.clone());
|
||||
self.config.cwd = event.cwd.clone();
|
||||
match AbsolutePathBuf::try_from(event.cwd.clone()) {
|
||||
Ok(cwd) => self.config.cwd = cwd,
|
||||
Err(err) => {
|
||||
tracing::warn!(path = %event.cwd.display(), %err, "session cwd should be absolute");
|
||||
}
|
||||
}
|
||||
if let Err(err) = self
|
||||
.config
|
||||
.permissions
|
||||
@@ -4026,13 +4032,13 @@ impl ChatWidget {
|
||||
id: ev.call_id,
|
||||
reason: ev.reason,
|
||||
changes: ev.changes.clone(),
|
||||
cwd: self.config.cwd.clone(),
|
||||
cwd: self.config.cwd.to_path_buf(),
|
||||
};
|
||||
self.bottom_pane
|
||||
.push_approval_request(request, &self.config.features);
|
||||
self.request_redraw();
|
||||
self.notify(Notification::EditApprovalRequested {
|
||||
cwd: self.config.cwd.clone(),
|
||||
cwd: self.config.cwd.to_path_buf(),
|
||||
changes: ev.changes.keys().cloned().collect(),
|
||||
});
|
||||
}
|
||||
@@ -4274,7 +4280,7 @@ impl ChatWidget {
|
||||
|
||||
let active_cell = Some(Self::placeholder_session_header_cell(&config));
|
||||
|
||||
let current_cwd = Some(config.cwd.clone());
|
||||
let current_cwd = Some(config.cwd.to_path_buf());
|
||||
let queued_message_edit_binding = queued_message_edit_binding_for_terminal(terminal_info());
|
||||
let mut widget = Self {
|
||||
app_event_tx: app_event_tx.clone(),
|
||||
@@ -4693,7 +4699,15 @@ impl ChatWidget {
|
||||
self.app_event_tx.send(AppEvent::ForkCurrentSession);
|
||||
}
|
||||
SlashCommand::Init => {
|
||||
let init_target = self.config.cwd.join(DEFAULT_PROJECT_DOC_FILENAME);
|
||||
let init_target = match self.config.cwd.join(DEFAULT_PROJECT_DOC_FILENAME) {
|
||||
Ok(path) => path,
|
||||
Err(err) => {
|
||||
self.add_error_message(format!(
|
||||
"Failed to prepare {DEFAULT_PROJECT_DOC_FILENAME}: {err}",
|
||||
));
|
||||
return;
|
||||
}
|
||||
};
|
||||
if init_target.exists() {
|
||||
let message = format!(
|
||||
"{DEFAULT_PROJECT_DOC_FILENAME} already exists here. Skipping /init to avoid overwriting it."
|
||||
@@ -5415,7 +5429,7 @@ impl ChatWidget {
|
||||
let service_tier = self.config.service_tier.map(Some);
|
||||
let op = AppCommand::user_turn(
|
||||
items,
|
||||
self.config.cwd.clone(),
|
||||
self.config.cwd.to_path_buf(),
|
||||
self.config.permissions.approval_policy.value(),
|
||||
self.config.permissions.sandbox_policy.get().clone(),
|
||||
effective_mode.model().to_string(),
|
||||
@@ -7022,7 +7036,9 @@ impl ChatWidget {
|
||||
}
|
||||
|
||||
fn status_line_cwd(&self) -> &Path {
|
||||
self.current_cwd.as_ref().unwrap_or(&self.config.cwd)
|
||||
self.current_cwd
|
||||
.as_deref()
|
||||
.unwrap_or(self.config.cwd.as_path())
|
||||
}
|
||||
|
||||
fn status_line_project_root(&self) -> Option<PathBuf> {
|
||||
@@ -9617,7 +9633,7 @@ impl ChatWidget {
|
||||
placeholder_style,
|
||||
/*reasoning_effort*/ None,
|
||||
/*show_fast_status*/ false,
|
||||
config.cwd.clone(),
|
||||
config.cwd.to_path_buf(),
|
||||
CODEX_CLI_VERSION,
|
||||
))
|
||||
}
|
||||
@@ -10331,7 +10347,7 @@ impl ChatWidget {
|
||||
name: "Review against a base branch".to_string(),
|
||||
description: Some("(PR Style)".into()),
|
||||
actions: vec![Box::new({
|
||||
let cwd = self.config.cwd.clone();
|
||||
let cwd = self.config.cwd.to_path_buf();
|
||||
move |tx| {
|
||||
tx.send(AppEvent::OpenReviewBranchPicker(cwd.clone()));
|
||||
}
|
||||
@@ -10356,7 +10372,7 @@ impl ChatWidget {
|
||||
items.push(SelectionItem {
|
||||
name: "Review a commit".to_string(),
|
||||
actions: vec![Box::new({
|
||||
let cwd = self.config.cwd.clone();
|
||||
let cwd = self.config.cwd.to_path_buf();
|
||||
move |tx| {
|
||||
tx.send(AppEvent::OpenReviewCommitPicker(cwd.clone()));
|
||||
}
|
||||
|
||||
@@ -159,11 +159,11 @@ impl ChatWidget {
|
||||
cwd: PathBuf,
|
||||
result: Result<PluginListResponse, String>,
|
||||
) {
|
||||
if self.plugins_fetch_state.in_flight_cwd.as_ref() == Some(&cwd) {
|
||||
if self.plugins_fetch_state.in_flight_cwd.as_deref() == Some(cwd.as_path()) {
|
||||
self.plugins_fetch_state.in_flight_cwd = None;
|
||||
}
|
||||
|
||||
if self.config.cwd != cwd {
|
||||
if self.config.cwd.as_path() != cwd.as_path() {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -191,13 +191,13 @@ impl ChatWidget {
|
||||
}
|
||||
|
||||
fn prefetch_plugins(&mut self) {
|
||||
let cwd = self.config.cwd.clone();
|
||||
if self.plugins_fetch_state.in_flight_cwd.as_ref() == Some(&cwd) {
|
||||
let cwd = self.config.cwd.to_path_buf();
|
||||
if self.plugins_fetch_state.in_flight_cwd.as_deref() == Some(cwd.as_path()) {
|
||||
return;
|
||||
}
|
||||
|
||||
self.plugins_fetch_state.in_flight_cwd = Some(cwd.clone());
|
||||
if self.plugins_fetch_state.cache_cwd.as_ref() != Some(&cwd) {
|
||||
if self.plugins_fetch_state.cache_cwd.as_deref() != Some(cwd.as_path()) {
|
||||
self.plugins_cache = PluginsCacheState::Loading;
|
||||
}
|
||||
|
||||
@@ -205,7 +205,7 @@ impl ChatWidget {
|
||||
}
|
||||
|
||||
fn plugins_cache_for_current_cwd(&self) -> PluginsCacheState {
|
||||
if self.plugins_fetch_state.cache_cwd.as_ref() == Some(&self.config.cwd) {
|
||||
if self.plugins_fetch_state.cache_cwd.as_deref() == Some(self.config.cwd.as_path()) {
|
||||
self.plugins_cache.clone()
|
||||
} else {
|
||||
PluginsCacheState::Uninitialized
|
||||
@@ -253,7 +253,7 @@ impl ChatWidget {
|
||||
cwd: PathBuf,
|
||||
result: Result<PluginReadResponse, String>,
|
||||
) {
|
||||
if self.config.cwd != cwd {
|
||||
if self.config.cwd.as_path() != cwd.as_path() {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -288,7 +288,7 @@ impl ChatWidget {
|
||||
plugin_display_name: String,
|
||||
result: Result<PluginInstallResponse, String>,
|
||||
) -> bool {
|
||||
if self.config.cwd != cwd {
|
||||
if self.config.cwd.as_path() != cwd.as_path() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -346,7 +346,7 @@ impl ChatWidget {
|
||||
plugin_display_name: String,
|
||||
result: Result<PluginUninstallResponse, String>,
|
||||
) {
|
||||
if self.config.cwd != cwd {
|
||||
if self.config.cwd.as_path() != cwd.as_path() {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -676,7 +676,7 @@ impl ChatWidget {
|
||||
..Default::default()
|
||||
}];
|
||||
if let Some(plugins_response) = plugins_response.cloned() {
|
||||
let cwd = self.config.cwd.clone();
|
||||
let cwd = self.config.cwd.to_path_buf();
|
||||
items.push(SelectionItem {
|
||||
name: "Back to plugins".to_string(),
|
||||
description: Some("Return to the plugin list.".to_string()),
|
||||
@@ -770,7 +770,7 @@ impl ChatWidget {
|
||||
"{display_name} {} {} {}",
|
||||
plugin.id, plugin.name, marketplace_label
|
||||
);
|
||||
let cwd = self.config.cwd.clone();
|
||||
let cwd = self.config.cwd.to_path_buf();
|
||||
let plugin_display_name = display_name.clone();
|
||||
let marketplace_path = marketplace.path.clone();
|
||||
let plugin_name = plugin.name.clone();
|
||||
@@ -861,7 +861,7 @@ impl ChatWidget {
|
||||
header.push(Line::from(description.dim()));
|
||||
}
|
||||
|
||||
let cwd = self.config.cwd.clone();
|
||||
let cwd = self.config.cwd.to_path_buf();
|
||||
let plugins_response = plugins_response.clone();
|
||||
let mut items = vec![SelectionItem {
|
||||
name: "Back to plugins".to_string(),
|
||||
@@ -877,7 +877,7 @@ impl ChatWidget {
|
||||
}];
|
||||
|
||||
if plugin.summary.installed {
|
||||
let uninstall_cwd = self.config.cwd.clone();
|
||||
let uninstall_cwd = self.config.cwd.to_path_buf();
|
||||
let plugin_id = plugin.summary.id.clone();
|
||||
let plugin_display_name = display_name;
|
||||
items.push(SelectionItem {
|
||||
@@ -906,7 +906,7 @@ impl ChatWidget {
|
||||
..Default::default()
|
||||
});
|
||||
} else {
|
||||
let install_cwd = self.config.cwd.clone();
|
||||
let install_cwd = self.config.cwd.to_path_buf();
|
||||
let marketplace_path = plugin.marketplace_path.clone();
|
||||
let plugin_name = plugin.summary.name.clone();
|
||||
let plugin_display_name = display_name;
|
||||
|
||||
@@ -17,6 +17,8 @@ use crate::chatwidget::realtime::RealtimeConversationPhase;
|
||||
use crate::history_cell::UserHistoryCell;
|
||||
use crate::model_catalog::ModelCatalog;
|
||||
use crate::test_backend::VT100Backend;
|
||||
use crate::test_support::PathBufExt;
|
||||
use crate::test_support::test_path_display;
|
||||
use crate::tui::FrameRequester;
|
||||
use assert_matches::assert_matches;
|
||||
use codex_app_server_protocol::AppSummary;
|
||||
@@ -458,10 +460,10 @@ async fn session_configured_syncs_widget_config_permissions_and_cwd() {
|
||||
.sandbox_policy
|
||||
.set(SandboxPolicy::new_workspace_write_policy())
|
||||
.expect("set sandbox policy");
|
||||
chat.config.cwd = PathBuf::from("/home/user/main");
|
||||
chat.config.cwd = PathBuf::from("/home/user/main").abs();
|
||||
|
||||
let expected_sandbox = SandboxPolicy::new_read_only_policy();
|
||||
let expected_cwd = PathBuf::from("/home/user/sub-agent");
|
||||
let expected_cwd = PathBuf::from("/home/user/sub-agent").abs();
|
||||
let configured = codex_protocol::protocol::SessionConfiguredEvent {
|
||||
session_id: ThreadId::new(),
|
||||
forked_from_id: None,
|
||||
@@ -472,7 +474,7 @@ async fn session_configured_syncs_widget_config_permissions_and_cwd() {
|
||||
approval_policy: AskForApproval::Never,
|
||||
approvals_reviewer: ApprovalsReviewer::User,
|
||||
sandbox_policy: expected_sandbox.clone(),
|
||||
cwd: expected_cwd.clone(),
|
||||
cwd: expected_cwd.to_path_buf(),
|
||||
reasoning_effort: Some(ReasoningEffortConfig::default()),
|
||||
history_log_id: 0,
|
||||
history_entry_count: 0,
|
||||
@@ -6426,7 +6428,7 @@ async fn slash_init_skips_when_project_doc_exists() {
|
||||
let tempdir = tempdir().unwrap();
|
||||
let existing_path = tempdir.path().join(DEFAULT_PROJECT_DOC_FILENAME);
|
||||
std::fs::write(&existing_path, "existing instructions").unwrap();
|
||||
chat.config.cwd = tempdir.path().to_path_buf();
|
||||
chat.config.cwd = tempdir.path().to_path_buf().abs();
|
||||
|
||||
chat.dispatch_command(SlashCommand::Init);
|
||||
|
||||
@@ -7419,13 +7421,17 @@ async fn custom_prompt_enter_empty_does_not_send() {
|
||||
#[tokio::test]
|
||||
async fn view_image_tool_call_adds_history_cell() {
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(None).await;
|
||||
let image_path = chat.config.cwd.join("example.png");
|
||||
let image_path = chat
|
||||
.config
|
||||
.cwd
|
||||
.join("example.png")
|
||||
.expect("absolute image path");
|
||||
|
||||
chat.handle_codex_event(Event {
|
||||
id: "sub-image".into(),
|
||||
msg: EventMsg::ViewImageToolCall(ViewImageToolCallEvent {
|
||||
call_id: "call-image".into(),
|
||||
path: image_path,
|
||||
path: image_path.to_path_buf(),
|
||||
}),
|
||||
});
|
||||
|
||||
@@ -7715,12 +7721,10 @@ fn strip_osc8_for_snapshot(text: &str) -> String {
|
||||
}
|
||||
|
||||
fn plugins_test_absolute_path(path: &str) -> AbsolutePathBuf {
|
||||
AbsolutePathBuf::try_from(
|
||||
std::env::temp_dir()
|
||||
.join("codex-plugin-menu-tests")
|
||||
.join(path),
|
||||
)
|
||||
.expect("expected absolute test path")
|
||||
std::env::temp_dir()
|
||||
.join("codex-plugin-menu-tests")
|
||||
.join(path)
|
||||
.abs()
|
||||
}
|
||||
|
||||
fn plugins_test_interface(
|
||||
@@ -7802,7 +7806,7 @@ fn plugins_test_response(marketplaces: Vec<PluginMarketplaceEntry>) -> PluginLis
|
||||
|
||||
fn render_loaded_plugins_popup(chat: &mut ChatWidget, response: PluginListResponse) -> String {
|
||||
let cwd = chat.config.cwd.clone();
|
||||
chat.on_plugins_loaded(cwd, Ok(response));
|
||||
chat.on_plugins_loaded(cwd.to_path_buf(), Ok(response));
|
||||
chat.add_plugins_output();
|
||||
render_bottom_popup(chat, 100)
|
||||
}
|
||||
@@ -7953,10 +7957,10 @@ async fn plugin_detail_popup_snapshot_shows_install_actions_and_capability_summa
|
||||
summary.clone(),
|
||||
])]);
|
||||
let cwd = chat.config.cwd.clone();
|
||||
chat.on_plugins_loaded(cwd.clone(), Ok(response));
|
||||
chat.on_plugins_loaded(cwd.to_path_buf(), Ok(response));
|
||||
chat.add_plugins_output();
|
||||
chat.on_plugin_detail_loaded(
|
||||
cwd,
|
||||
cwd.to_path_buf(),
|
||||
Ok(PluginReadResponse {
|
||||
plugin: plugins_test_detail(
|
||||
summary,
|
||||
@@ -7993,10 +7997,10 @@ async fn plugin_detail_popup_hides_disclosure_for_installed_plugins() {
|
||||
summary.clone(),
|
||||
])]);
|
||||
let cwd = chat.config.cwd.clone();
|
||||
chat.on_plugins_loaded(cwd.clone(), Ok(response));
|
||||
chat.on_plugins_loaded(cwd.to_path_buf(), Ok(response));
|
||||
chat.add_plugins_output();
|
||||
chat.on_plugin_detail_loaded(
|
||||
cwd,
|
||||
cwd.to_path_buf(),
|
||||
Ok(PluginReadResponse {
|
||||
plugin: plugins_test_detail(
|
||||
summary,
|
||||
@@ -8083,7 +8087,7 @@ async fn plugins_popup_refresh_replaces_selection_with_first_row() {
|
||||
),
|
||||
])]);
|
||||
let cwd = chat.config.cwd.clone();
|
||||
chat.on_plugins_loaded(cwd, Ok(refreshed));
|
||||
chat.on_plugins_loaded(cwd.to_path_buf(), Ok(refreshed));
|
||||
|
||||
let after = render_bottom_popup(&chat, 100);
|
||||
assert!(
|
||||
@@ -8152,7 +8156,7 @@ async fn plugins_popup_refreshes_installed_counts_after_install() {
|
||||
),
|
||||
])]);
|
||||
let cwd = chat.config.cwd.clone();
|
||||
chat.on_plugins_loaded(cwd, Ok(refreshed));
|
||||
chat.on_plugins_loaded(cwd.to_path_buf(), Ok(refreshed));
|
||||
|
||||
let after = render_bottom_popup(&chat, 100);
|
||||
assert!(
|
||||
@@ -8824,8 +8828,7 @@ async fn apps_initial_load_applies_enabled_state_from_config() {
|
||||
chat.bottom_pane.set_connectors_enabled(true);
|
||||
|
||||
let temp = tempdir().expect("tempdir");
|
||||
let config_toml_path =
|
||||
AbsolutePathBuf::try_from(temp.path().join("config.toml")).expect("absolute config path");
|
||||
let config_toml_path = temp.path().join("config.toml").abs();
|
||||
let user_config = toml::from_str::<TomlValue>(
|
||||
"[apps.connector_1]\nenabled = false\ndisabled_reason = \"user\"\n",
|
||||
)
|
||||
@@ -8889,8 +8892,7 @@ async fn apps_initial_load_applies_enabled_state_from_requirements_with_user_ove
|
||||
..Default::default()
|
||||
};
|
||||
let temp = tempdir().expect("tempdir");
|
||||
let config_toml_path =
|
||||
AbsolutePathBuf::try_from(temp.path().join("config.toml")).expect("absolute config path");
|
||||
let config_toml_path = temp.path().join("config.toml").abs();
|
||||
chat.config.config_layer_stack =
|
||||
ConfigLayerStack::new(Vec::new(), ConfigRequirements::default(), requirements)
|
||||
.expect("requirements stack")
|
||||
@@ -9465,7 +9467,7 @@ async fn preset_matching_accepts_workspace_write_with_extra_roots() {
|
||||
.find(|p| p.id == "auto")
|
||||
.expect("auto preset exists");
|
||||
let current_sandbox = SandboxPolicy::WorkspaceWrite {
|
||||
writable_roots: vec![AbsolutePathBuf::try_from("C:\\extra").unwrap()],
|
||||
writable_roots: vec![PathBuf::from("C:\\extra").abs()],
|
||||
read_only_access: Default::default(),
|
||||
network_access: false,
|
||||
exclude_tmpdir_env_var: false,
|
||||
@@ -10265,8 +10267,7 @@ async fn permissions_selection_marks_guardian_approvals_current_with_custom_work
|
||||
.features
|
||||
.set_enabled(Feature::GuardianApproval, true);
|
||||
|
||||
let extra_root = AbsolutePathBuf::try_from("/tmp/guardian-approvals-extra")
|
||||
.expect("absolute extra writable root");
|
||||
let extra_root = PathBuf::from("/tmp/guardian-approvals-extra").abs();
|
||||
|
||||
chat.handle_codex_event(Event {
|
||||
id: "session-configured-custom-workspace".to_string(),
|
||||
@@ -12279,7 +12280,7 @@ async fn status_line_fast_mode_footer_snapshot() {
|
||||
#[tokio::test]
|
||||
async fn status_line_model_with_reasoning_includes_fast_for_gpt54_only() {
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.4")).await;
|
||||
chat.config.cwd = PathBuf::from("/tmp/project");
|
||||
chat.config.cwd = PathBuf::from("/tmp/project").abs();
|
||||
chat.config.tui_status_line = Some(vec![
|
||||
"model-with-reasoning".to_string(),
|
||||
"context-remaining".to_string(),
|
||||
@@ -12289,10 +12290,11 @@ async fn status_line_model_with_reasoning_includes_fast_for_gpt54_only() {
|
||||
chat.set_service_tier(Some(ServiceTier::Fast));
|
||||
set_chatgpt_auth(&mut chat);
|
||||
chat.refresh_status_line();
|
||||
let test_cwd = test_path_display("/tmp/project");
|
||||
|
||||
assert_eq!(
|
||||
status_line_text(&chat),
|
||||
Some("gpt-5.4 xhigh fast · 100% left · /tmp/project".to_string())
|
||||
Some(format!("gpt-5.4 xhigh fast · 100% left · {test_cwd}"))
|
||||
);
|
||||
|
||||
chat.set_model("gpt-5.3-codex");
|
||||
@@ -12300,18 +12302,22 @@ async fn status_line_model_with_reasoning_includes_fast_for_gpt54_only() {
|
||||
|
||||
assert_eq!(
|
||||
status_line_text(&chat),
|
||||
Some("gpt-5.3-codex xhigh · 100% left · /tmp/project".to_string())
|
||||
Some(format!("gpt-5.3-codex xhigh · 100% left · {test_cwd}"))
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
#[cfg_attr(
|
||||
target_os = "windows",
|
||||
ignore = "snapshot path rendering differs on Windows"
|
||||
)]
|
||||
async fn status_line_model_with_reasoning_fast_footer_snapshot() {
|
||||
use ratatui::Terminal;
|
||||
use ratatui::backend::TestBackend;
|
||||
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.4")).await;
|
||||
chat.show_welcome_banner = false;
|
||||
chat.config.cwd = PathBuf::from("/tmp/project");
|
||||
chat.config.cwd = PathBuf::from("/tmp/project").abs();
|
||||
chat.config.tui_status_line = Some(vec![
|
||||
"model-with-reasoning".to_string(),
|
||||
"context-remaining".to_string(),
|
||||
|
||||
@@ -27,6 +27,8 @@ use crate::render::line_utils::push_owned_lines;
|
||||
use crate::render::renderable::Renderable;
|
||||
use crate::style::proposed_plan_style;
|
||||
use crate::style::user_message_style;
|
||||
#[cfg(test)]
|
||||
use crate::test_support::PathBufExt;
|
||||
use crate::text_formatting::format_and_truncate_tool_result;
|
||||
use crate::text_formatting::truncate_text;
|
||||
use crate::tooltips;
|
||||
@@ -1143,7 +1145,7 @@ pub(crate) fn new_session_info(
|
||||
model.clone(),
|
||||
reasoning_effort,
|
||||
show_fast_status,
|
||||
config.cwd.clone(),
|
||||
config.cwd.to_path_buf(),
|
||||
CODEX_CLI_VERSION,
|
||||
);
|
||||
let mut parts: Vec<Box<dyn HistoryCell>> = vec![Box::new(header)];
|
||||
@@ -2883,7 +2885,7 @@ mod tests {
|
||||
approval_policy: AskForApproval::Never,
|
||||
approvals_reviewer: codex_protocol::config_types::ApprovalsReviewer::User,
|
||||
sandbox_policy: SandboxPolicy::new_read_only_policy(),
|
||||
cwd: PathBuf::from("/tmp/project"),
|
||||
cwd: PathBuf::from("/tmp/project").abs().to_path_buf(),
|
||||
reasoning_effort: None,
|
||||
history_log_id: 0,
|
||||
history_entry_count: 0,
|
||||
@@ -2997,9 +2999,13 @@ mod tests {
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
#[cfg_attr(
|
||||
target_os = "windows",
|
||||
ignore = "snapshot path rendering differs on Windows"
|
||||
)]
|
||||
async fn session_info_availability_nux_tooltip_snapshot() {
|
||||
let mut config = test_config().await;
|
||||
config.cwd = PathBuf::from("/tmp/project");
|
||||
config.cwd = PathBuf::from("/tmp/project").abs();
|
||||
let cell = new_session_info(
|
||||
&config,
|
||||
"gpt-5",
|
||||
|
||||
@@ -248,6 +248,8 @@ mod wrapping;
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod test_backend;
|
||||
#[cfg(test)]
|
||||
pub(crate) mod test_support;
|
||||
|
||||
use crate::onboarding::onboarding_screen::OnboardingScreenArgs;
|
||||
use crate::onboarding::onboarding_screen::run_onboarding_app;
|
||||
@@ -1221,7 +1223,7 @@ async fn run_ratatui_app(
|
||||
let fallback_cwd = match action_and_target_session_if_resume_or_fork {
|
||||
Some((action, target_session)) => {
|
||||
if remote_mode {
|
||||
Some(current_cwd.clone())
|
||||
Some(current_cwd.to_path_buf())
|
||||
} else {
|
||||
match resolve_cwd_for_resume_or_fork(
|
||||
&mut tui,
|
||||
|
||||
@@ -85,7 +85,7 @@ impl OnboardingScreen {
|
||||
app_server_request_handle,
|
||||
config,
|
||||
} = args;
|
||||
let cwd = config.cwd.clone();
|
||||
let cwd = config.cwd.to_path_buf();
|
||||
let forced_chatgpt_workspace_id = config.forced_chatgpt_workspace_id.clone();
|
||||
let forced_login_method = config.forced_login_method;
|
||||
let codex_home = config.codex_home.clone();
|
||||
|
||||
@@ -255,7 +255,7 @@ impl StatusHistoryCell {
|
||||
Self {
|
||||
model_name,
|
||||
model_details,
|
||||
directory: config.cwd.clone(),
|
||||
directory: config.cwd.to_path_buf(),
|
||||
permissions,
|
||||
agents_summary,
|
||||
collaboration_mode: collaboration_mode.map(ToString::to_string),
|
||||
|
||||
@@ -42,7 +42,7 @@ pub(crate) fn compose_agents_summary(config: &Config) -> String {
|
||||
.map(|name| name.to_string_lossy().to_string())
|
||||
.unwrap_or_else(|| "<unknown>".to_string());
|
||||
let display = if let Some(parent) = p.parent() {
|
||||
if parent == config.cwd {
|
||||
if parent == config.cwd.as_path() {
|
||||
file_name.clone()
|
||||
} else {
|
||||
let mut cur = config.cwd.as_path();
|
||||
|
||||
@@ -2,6 +2,7 @@ use super::new_status_output;
|
||||
use super::rate_limit_snapshot_display;
|
||||
use crate::history_cell::HistoryCell;
|
||||
use crate::status::StatusAccountDisplay;
|
||||
use crate::test_support::PathBufExt;
|
||||
use chrono::Duration as ChronoDuration;
|
||||
use chrono::TimeZone;
|
||||
use chrono::Utc;
|
||||
@@ -105,7 +106,7 @@ async fn status_snapshot_includes_reasoning_details() {
|
||||
})
|
||||
.expect("set sandbox policy");
|
||||
|
||||
config.cwd = PathBuf::from("/workspace/tests");
|
||||
config.cwd = PathBuf::from("/workspace/tests").abs();
|
||||
|
||||
let account_display = test_status_account_display();
|
||||
let usage = TokenUsage {
|
||||
@@ -189,7 +190,7 @@ async fn status_permissions_non_default_workspace_write_is_custom() {
|
||||
exclude_slash_tmp: false,
|
||||
})
|
||||
.expect("set sandbox policy");
|
||||
config.cwd = PathBuf::from("/workspace/tests");
|
||||
config.cwd = PathBuf::from("/workspace/tests").abs();
|
||||
|
||||
let account_display = test_status_account_display();
|
||||
let usage = TokenUsage::default();
|
||||
@@ -238,7 +239,7 @@ async fn status_snapshot_includes_forked_from() {
|
||||
let mut config = test_config(&temp_home).await;
|
||||
config.model = Some("gpt-5.1-codex-max".to_string());
|
||||
config.model_provider_id = "openai".to_string();
|
||||
config.cwd = PathBuf::from("/workspace/tests");
|
||||
config.cwd = PathBuf::from("/workspace/tests").abs();
|
||||
|
||||
let account_display = test_status_account_display();
|
||||
let usage = TokenUsage {
|
||||
@@ -292,7 +293,7 @@ async fn status_snapshot_includes_monthly_limit() {
|
||||
let mut config = test_config(&temp_home).await;
|
||||
config.model = Some("gpt-5.1-codex-max".to_string());
|
||||
config.model_provider_id = "openai".to_string();
|
||||
config.cwd = PathBuf::from("/workspace/tests");
|
||||
config.cwd = PathBuf::from("/workspace/tests").abs();
|
||||
|
||||
let account_display = test_status_account_display();
|
||||
let usage = TokenUsage {
|
||||
@@ -545,7 +546,7 @@ async fn status_card_token_usage_excludes_cached_tokens() {
|
||||
let temp_home = TempDir::new().expect("temp home");
|
||||
let mut config = test_config(&temp_home).await;
|
||||
config.model = Some("gpt-5.1-codex-max".to_string());
|
||||
config.cwd = PathBuf::from("/workspace/tests");
|
||||
config.cwd = PathBuf::from("/workspace/tests").abs();
|
||||
|
||||
let account_display = test_status_account_display();
|
||||
let usage = TokenUsage {
|
||||
@@ -593,7 +594,7 @@ async fn status_snapshot_truncates_in_narrow_terminal() {
|
||||
config.model = Some("gpt-5.1-codex-max".to_string());
|
||||
config.model_provider_id = "openai".to_string();
|
||||
config.model_reasoning_summary = Some(ReasoningSummary::Detailed);
|
||||
config.cwd = PathBuf::from("/workspace/tests");
|
||||
config.cwd = PathBuf::from("/workspace/tests").abs();
|
||||
|
||||
let account_display = test_status_account_display();
|
||||
let usage = TokenUsage {
|
||||
@@ -656,7 +657,7 @@ async fn status_snapshot_shows_missing_limits_message() {
|
||||
let temp_home = TempDir::new().expect("temp home");
|
||||
let mut config = test_config(&temp_home).await;
|
||||
config.model = Some("gpt-5.1-codex-max".to_string());
|
||||
config.cwd = PathBuf::from("/workspace/tests");
|
||||
config.cwd = PathBuf::from("/workspace/tests").abs();
|
||||
|
||||
let account_display = test_status_account_display();
|
||||
let usage = TokenUsage {
|
||||
@@ -704,7 +705,7 @@ async fn status_snapshot_includes_credits_and_limits() {
|
||||
let temp_home = TempDir::new().expect("temp home");
|
||||
let mut config = test_config(&temp_home).await;
|
||||
config.model = Some("gpt-5.1-codex".to_string());
|
||||
config.cwd = PathBuf::from("/workspace/tests");
|
||||
config.cwd = PathBuf::from("/workspace/tests").abs();
|
||||
|
||||
let account_display = test_status_account_display();
|
||||
let usage = TokenUsage {
|
||||
@@ -773,7 +774,7 @@ async fn status_snapshot_shows_empty_limits_message() {
|
||||
let temp_home = TempDir::new().expect("temp home");
|
||||
let mut config = test_config(&temp_home).await;
|
||||
config.model = Some("gpt-5.1-codex-max".to_string());
|
||||
config.cwd = PathBuf::from("/workspace/tests");
|
||||
config.cwd = PathBuf::from("/workspace/tests").abs();
|
||||
|
||||
let account_display = test_status_account_display();
|
||||
let usage = TokenUsage {
|
||||
@@ -830,7 +831,7 @@ async fn status_snapshot_shows_stale_limits_message() {
|
||||
let temp_home = TempDir::new().expect("temp home");
|
||||
let mut config = test_config(&temp_home).await;
|
||||
config.model = Some("gpt-5.1-codex-max".to_string());
|
||||
config.cwd = PathBuf::from("/workspace/tests");
|
||||
config.cwd = PathBuf::from("/workspace/tests").abs();
|
||||
|
||||
let account_display = test_status_account_display();
|
||||
let usage = TokenUsage {
|
||||
@@ -896,7 +897,7 @@ async fn status_snapshot_cached_limits_hide_credits_without_flag() {
|
||||
let temp_home = TempDir::new().expect("temp home");
|
||||
let mut config = test_config(&temp_home).await;
|
||||
config.model = Some("gpt-5.1-codex".to_string());
|
||||
config.cwd = PathBuf::from("/workspace/tests");
|
||||
config.cwd = PathBuf::from("/workspace/tests").abs();
|
||||
|
||||
let account_display = test_status_account_display();
|
||||
let usage = TokenUsage {
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
|
||||
pub(crate) trait PathExt {
|
||||
fn abs(&self) -> AbsolutePathBuf;
|
||||
}
|
||||
|
||||
impl PathExt for Path {
|
||||
fn abs(&self) -> AbsolutePathBuf {
|
||||
AbsolutePathBuf::try_from(self.to_path_buf())
|
||||
.unwrap_or_else(|_| panic!("path should already be absolute"))
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) trait PathBufExt {
|
||||
fn abs(&self) -> AbsolutePathBuf;
|
||||
}
|
||||
|
||||
impl PathBufExt for PathBuf {
|
||||
fn abs(&self) -> AbsolutePathBuf {
|
||||
self.as_path().abs()
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn test_path_display(path: &str) -> String {
|
||||
Path::new(path).abs().display().to_string()
|
||||
}
|
||||
Reference in New Issue
Block a user