use crate::core::refs::with_ref_manager;
use crate::core;
use crate::core::versions::MinOxenVersion;
use crate::error::OxenError;
use crate::model::{Commit, LocalRepository, ParsedResource};
use crate::repositories;
use std::path::Path;
pub fn parse_resource_from_path(
repo: &LocalRepository,
path: &Path,
) -> Result<Option<ParsedResource>, OxenError> {
match repo.min_version() {
MinOxenVersion::V0_10_0 => panic!("v0.10.0 no longer supported"),
_ => core::v_latest::resource::parse_resource_from_path(repo, path),
}
}
pub fn maybe_get_commit_id_from_branch_name<S: AsRef<str>>(
repo: &LocalRepository,
commit_id_or_branch_name: S,
) -> Result<Option<String>, OxenError> {
with_ref_manager(repo, |manager| {
manager.get_commit_id_for_branch(commit_id_or_branch_name.as_ref())
})
}
pub fn maybe_get_commit<S: AsRef<str>>(
repo: &LocalRepository,
commit_id_or_branch_name: S,
) -> Result<Option<Commit>, OxenError> {
if let Some(commit) = repositories::commits::get_by_id(repo, &commit_id_or_branch_name)? {
return Ok(Some(commit));
}
match maybe_get_commit_id_from_branch_name(repo, &commit_id_or_branch_name) {
Ok(Some(commit_id)) => repositories::commits::get_by_id(repo, &commit_id),
Ok(None) => Err(OxenError::local_revision_not_found(
commit_id_or_branch_name.as_ref(),
)),
Err(err) => Err(err),
}
}
pub fn get_commit_or_head<S: AsRef<str>>(
repo: &LocalRepository,
commit_id_or_branch_name: Option<S>,
) -> Result<Commit, OxenError> {
if commit_id_or_branch_name.is_none() {
return repositories::commits::head_commit(repo);
}
match maybe_get_commit(repo, commit_id_or_branch_name.unwrap().as_ref()) {
Ok(Some(commit)) => Ok(commit),
_ => repositories::commits::head_commit(repo),
}
}
#[cfg(test)]
mod tests {
use crate::test;
use std::path::Path;
use crate::error::OxenError;
use crate::repositories;
use crate::resource;
#[tokio::test]
async fn test_parse_resource_for_commit() -> Result<(), OxenError> {
test::run_training_data_repo_test_fully_committed_async(|repo| async move {
let history = repositories::commits::list(&repo)?;
let commit = history.first().unwrap();
let path_str = format!("{}/annotations/train/one_shot.csv", commit.id);
let path = Path::new(&path_str);
match resource::parse_resource_from_path(&repo, path) {
Ok(Some(resource)) => {
assert_eq!(commit.id, resource.commit.unwrap().id);
assert_eq!(resource.path, Path::new("annotations/train/one_shot.csv"));
}
_ => {
panic!("Should return a commit");
}
}
Ok(())
})
.await
}
#[tokio::test]
async fn test_parse_resource_for_branch() -> Result<(), OxenError> {
test::run_training_data_repo_test_fully_committed_async(|repo| async move {
let branch_name = "my-branch";
let branch = repositories::branches::create_checkout(&repo, branch_name)?;
let path_str = format!("{branch_name}/annotations/train/one_shot.csv");
let path = Path::new(&path_str);
match resource::parse_resource_from_path(&repo, path) {
Ok(Some(resource)) => {
println!("Got branch: {branch:?} -> {path:?}");
assert_eq!(branch.commit_id, resource.commit.unwrap().id);
assert_eq!(resource.path, Path::new("annotations/train/one_shot.csv"));
}
_ => {
panic!("Should return a branch");
}
}
Ok(())
})
.await
}
#[tokio::test]
async fn test_parse_resource_for_long_branch_name() -> Result<(), OxenError> {
test::run_training_data_repo_test_fully_committed_async(|repo| async move {
let branch_name = "my/crazy/branch/name";
let branch = repositories::branches::create_checkout(&repo, branch_name)?;
let path_str = format!("{branch_name}/annotations/train/one_shot.csv");
let path = Path::new(&path_str);
match resource::parse_resource_from_path(&repo, path) {
Ok(Some(resource)) => {
println!("Got branch: {branch:?} -> {path:?}");
assert_eq!(branch.commit_id, resource.commit.unwrap().id);
assert_eq!(resource.path, Path::new("annotations/train/one_shot.csv"));
}
_ => {
panic!("Should return a branch");
}
}
Ok(())
})
.await
}
#[tokio::test]
async fn test_parse_resource_for_branch_base_dir() -> Result<(), OxenError> {
test::run_training_data_repo_test_fully_committed_async(|repo| async move {
let branch_name = "my_branch";
let branch = repositories::branches::create_checkout(&repo, branch_name)?;
let path_str = branch_name.to_string();
let path = Path::new(&path_str);
match resource::parse_resource_from_path(&repo, path) {
Ok(Some(resource)) => {
println!("Got branch: {branch:?} -> {path:?}");
assert_eq!(branch.commit_id, resource.commit.unwrap().id);
assert_eq!(resource.path, Path::new(""));
}
_ => {
panic!("Should return a branch");
}
}
Ok(())
})
.await
}
#[tokio::test]
async fn test_parse_resource_from_path_root_dir() -> Result<(), OxenError> {
test::run_training_data_repo_test_fully_committed_async(|repo| async move {
let branch_name = "main";
let path_str = format!("{branch_name}/");
let path = Path::new(&path_str);
match resource::parse_resource_from_path(&repo, path) {
Ok(Some(resource)) => {
assert_eq!(resource.path, Path::new(""))
}
_ => {
panic!("Should return a parsed resource");
}
}
Ok(())
})
.await
}
#[tokio::test]
async fn test_parse_resource_from_path_root_dir_complicated_branch() -> Result<(), OxenError> {
test::run_training_data_repo_test_fully_committed_async(|repo| async move {
let branch_name = "super/complex/branch-name/slashes";
let _branch = repositories::branches::create_checkout(&repo, branch_name)?;
let path_str = format!("{branch_name}/");
let path = Path::new(&path_str);
if !cfg!(windows) {
match resource::parse_resource_from_path(&repo, path) {
Ok(Some(resource)) => {
assert_eq!(resource.path, Path::new(""))
}
_ => {
panic!("Should return a parsed resource");
}
}
}
Ok(())
})
.await
}
#[tokio::test]
async fn test_parse_resource_from_path_nonroot_complicated_branch() -> Result<(), OxenError> {
test::run_training_data_repo_test_fully_committed_async(|repo| async move {
let branch_name = "super/complex/branch-name/slashes";
let _branch = repositories::branches::create_checkout(&repo, branch_name)?;
let path = Path::new(branch_name).join("folder-new");
if !cfg!(windows) {
match resource::parse_resource_from_path(&repo, &path) {
Ok(Some(resource)) => {
assert_eq!(resource.path, Path::new("folder-new"))
}
_ => {
panic!("Should return a parsed resource");
}
}
}
Ok(())
})
.await
}
#[tokio::test]
async fn test_parse_resource_from_path_with_file() -> Result<(), OxenError> {
test::run_training_data_repo_test_fully_committed_async(|repo| async move {
let branch_name = "super/complex/branch-name/slashes";
let _branch = repositories::branches::create_checkout(&repo, branch_name)?;
let path_str = format!("{branch_name}/folder/item.txt");
let path = Path::new(&path_str);
if !cfg!(windows) {
match resource::parse_resource_from_path(&repo, path) {
Ok(Some(resource)) => {
assert_eq!(resource.path, Path::new("folder/item.txt"))
}
_ => {
panic!("Should return a parsed resource");
}
}
}
Ok(())
})
.await
}
#[tokio::test]
async fn test_parse_resource_commit_wins_over_branch() -> Result<(), OxenError> {
test::run_one_commit_local_repo_test_async(|repo| async move {
let commit = repositories::commits::head_commit(&repo)?;
repositories::branches::create_from_head(&repo, &commit.id)?;
let path_str = format!("{}/some/file.txt", commit.id);
let path = Path::new(&path_str);
let resource =
resource::parse_resource_from_path(&repo, path)?.expect("should resolve");
assert!(resource.commit.is_some(), "expected commit");
assert!(resource.branch.is_none(), "should not resolve as branch");
assert_eq!(resource.commit.unwrap().id, commit.id);
assert_eq!(resource.path, Path::new("some/file.txt"));
Ok(())
})
.await
}
#[tokio::test]
async fn test_parse_resource_branch_wins_over_workspace() -> Result<(), OxenError> {
test::run_one_commit_local_repo_test_async(|repo| async move {
let commit = repositories::commits::head_commit(&repo)?;
let shared_name = "shared-name";
let branch = repositories::branches::create_from_head(&repo, shared_name)?;
let _workspace = repositories::workspaces::create(&repo, &commit, shared_name, false)?;
let path_str = format!("{shared_name}/data.csv");
let path = Path::new(&path_str);
let resource =
resource::parse_resource_from_path(&repo, path)?.expect("should resolve");
assert!(resource.branch.is_some(), "expected branch");
assert!(
resource.workspace.is_none(),
"should not resolve as workspace"
);
assert_eq!(resource.branch.unwrap().name, branch.name);
assert_eq!(resource.path, Path::new("data.csv"));
Ok(())
})
.await
}
#[tokio::test]
async fn test_parse_resource_workspace_resolves_when_no_branch() -> Result<(), OxenError> {
test::run_one_commit_local_repo_test_async(|repo| async move {
let commit = repositories::commits::head_commit(&repo)?;
let workspace = repositories::workspaces::create(&repo, &commit, "my-workspace", true)?;
let path_str = format!("{}/data.csv", workspace.id);
let path = Path::new(&path_str);
let resource =
resource::parse_resource_from_path(&repo, path)?.expect("should resolve");
assert!(resource.workspace.is_some(), "expected workspace");
assert!(resource.branch.is_none());
assert!(resource.commit.is_none());
assert_eq!(resource.workspace.unwrap().id, workspace.id);
assert_eq!(resource.path, Path::new("data.csv"));
Ok(())
})
.await
}
#[tokio::test]
async fn test_parse_resource_full_branch_path_match() -> Result<(), OxenError> {
test::run_one_commit_local_repo_test_async(|repo| async move {
let branch_name = "feature/v2/experiment";
let branch = repositories::branches::create_from_head(&repo, branch_name)?;
let path = Path::new(branch_name);
let resource =
resource::parse_resource_from_path(&repo, path)?.expect("should resolve");
assert!(resource.branch.is_some());
assert_eq!(resource.branch.unwrap().name, branch.name);
assert_eq!(resource.path, Path::new(""));
Ok(())
})
.await
}
#[tokio::test]
async fn test_parse_resource_returns_none_for_unknown() -> Result<(), OxenError> {
test::run_one_commit_local_repo_test_async(|repo| async move {
let path = Path::new("nonexistent-thing/file.txt");
let result = resource::parse_resource_from_path(&repo, path)?;
assert!(result.is_none(), "unknown identifier should return None");
Ok(())
})
.await
}
}