use crate::api::client::repositories::get_by_remote;
use crate::error::OxenError;
use crate::model::{LocalRepository, Remote};
fn reject_unusable_remote(repo: &LocalRepository, url: &str) -> Result<(), OxenError> {
if url::Url::parse(url).is_err() {
return Err(OxenError::invalid_set_remote_url(url));
}
if repo.is_remote_mode() {
return Err(OxenError::basic_str(
"Error: Cannot change remote of remote-mode repos",
));
}
Ok(())
}
#[cfg(any(test, feature = "test-utils"))]
pub fn set_remote(repo: &mut LocalRepository, name: &str, url: &str) -> Result<Remote, OxenError> {
reject_unusable_remote(repo, url)?;
let remote = repo.set_remote(name, url);
repo.save()?;
Ok(remote)
}
pub async fn set_remote_by_url(
repo: &mut LocalRepository,
name: &str,
url: &str,
) -> Result<Remote, OxenError> {
reject_unusable_remote(repo, url)?;
let requested = Remote {
name: name.to_string(),
url: url.to_string(),
repo_uuid: repo
.get_remote(name)
.and_then(|recorded| recorded.repo_uuid),
};
let remote_repo = get_by_remote(&requested).await?;
let remote = repo.set_remote_repo(name, &remote_repo);
repo.save()?;
Ok(remote)
}
pub fn delete_remote(repo: &mut LocalRepository, name: &str) -> Result<(), OxenError> {
if repo.is_remote_mode() {
return Err(OxenError::basic_str(
"Error: Cannot delete from remote of remote-mode repos",
));
}
repo.delete_remote(name);
repo.save()?;
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use crate::test;
use uuid::Uuid;
#[cfg_attr(windows, ignore = "oxen-server is not supported on Windows")]
#[tokio::test]
async fn test_set_remote_by_url_records_the_servers_uuid() -> Result<(), OxenError> {
test::run_empty_remote_repo_test(|mut repo, remote_repo| async move {
let expected = remote_repo
.remote
.repo_uuid
.expect("the server reports a UUID for a repo it created");
let remote = set_remote_by_url(&mut repo, "origin", &remote_repo.remote.url).await?;
assert_eq!(remote.repo_uuid, Some(expected));
Ok(remote_repo)
})
.await
}
#[cfg_attr(windows, ignore = "oxen-server is not supported on Windows")]
#[tokio::test]
async fn test_set_remote_by_url_refuses_a_different_repo() -> Result<(), OxenError> {
test::run_empty_remote_repo_test(|mut repo, remote_repo| async move {
let mut attached = remote_repo.clone();
attached.remote.repo_uuid = Some(Uuid::new_v4());
test::attach_remote_repo(&mut repo, &attached)?;
let result = set_remote_by_url(&mut repo, "origin", &remote_repo.remote.url).await;
assert!(
matches!(result, Err(OxenError::RemotePointsAtDifferentRepo { .. })),
"expected a refusal, got: {result:?}"
);
Ok(remote_repo)
})
.await
}
#[tokio::test]
async fn test_set_remote_by_url_errors_when_the_server_cannot_be_read() -> Result<(), OxenError>
{
test::run_empty_local_repo_test_async(|mut repo| async move {
let url = "http://localhost:1/ox/cats";
let err = set_remote_by_url(&mut repo, "origin", url)
.await
.expect_err("nothing accepts connections on port 1");
assert!(
matches!(err, OxenError::HTTP(_)),
"expected a failed read rather than local URL validation: {err}"
);
assert!(
repo.remotes().is_empty(),
"a failed attach records nothing: {:?}",
repo.remotes()
);
Ok(())
})
.await
}
}