use crate::prelude::*;
use crate::state::{
calculate_workspace_version, update_version_file_in_repo, StateConfig, TemplateManager,
};
use crate::workspace_state::WorkspaceState;
#[derive(Debug, Clone)]
pub struct WorkspaceUpdateResult {
pub version: String,
pub rendered_tera_files: Vec<String>,
pub rendered_wstemplate_files: Vec<String>,
}
pub fn run_workspace_update(project_root: &Path) -> Result<WorkspaceUpdateResult> {
let config = StateConfig::load(project_root)?;
let workspace_state = WorkspaceState::load(project_root)?;
let version_info = calculate_workspace_version(project_root)?;
update_version_file_in_repo(&version_info, &config, project_root)?;
let template_manager = TemplateManager::new(&workspace_state)?;
let project_name = workspace_state.project_name.as_deref();
let rendered_tera_files = template_manager.render_all_templates(&version_info, project_name)?;
let entry = workspace_state.effective_wstemplate_entry();
let engine = crate::state::wstemplate::WstemplateEngine::new(
version_info.clone(),
workspace_state.project_name.clone(),
entry.alias,
project_root.to_path_buf(),
entry.root,
);
let rendered = engine.render_relevant()?;
let rendered_wstemplate_files = rendered
.into_iter()
.map(|item| {
item.output_path
.strip_prefix(project_root)
.unwrap_or(item.output_path.as_path())
.display()
.to_string()
})
.collect::<Vec<_>>();
workspace_state
.save(project_root)
.with_context(|| format!("failed to save workspace state in {}", project_root.display()))?;
Ok(WorkspaceUpdateResult {
version: version_info.full_version,
rendered_tera_files,
rendered_wstemplate_files,
})
}
pub async fn run_workspace_update_async(project_root: &Path) -> Result<WorkspaceUpdateResult> {
let config = StateConfig::load_async(project_root).await?;
let workspace_state = WorkspaceState::load(project_root)?;
let version_info = crate::state::calculate_workspace_version_async(project_root).await?;
update_version_file_in_repo(&version_info, &config, project_root)?;
let template_manager = TemplateManager::new(&workspace_state)?;
let project_name = workspace_state.project_name.as_deref();
let rendered_tera_files = template_manager.render_all_templates(&version_info, project_name)?;
let entry = workspace_state.effective_wstemplate_entry();
let engine = crate::state::wstemplate::WstemplateEngine::new(
version_info.clone(),
workspace_state.project_name.clone(),
entry.alias,
project_root.to_path_buf(),
entry.root,
);
let rendered = engine.render_relevant()?;
let rendered_wstemplate_files = rendered
.into_iter()
.map(|item| {
item.output_path
.strip_prefix(project_root)
.unwrap_or(item.output_path.as_path())
.display()
.to_string()
})
.collect::<Vec<_>>();
workspace_state
.save(project_root)
.with_context(|| format!("failed to save workspace state in {}", project_root.display()))?;
Ok(WorkspaceUpdateResult {
version: version_info.full_version,
rendered_tera_files,
rendered_wstemplate_files,
})
}
#[cfg(test)]
mod tests {
use super::*;
use std::fs;
use tempfile::TempDir;
fn run_git(repo_root: &Path, args: &[&str]) {
let repo = match args {
["init"] => crate::git_backend::init_repository(repo_root).unwrap(),
_ => crate::git_backend::open_repo(repo_root).unwrap(),
};
match args {
["init"] => {}
["config", "user.name", name] => {
let email = repo
.config_snapshot()
.string("user.email")
.map(|value| value.to_string())
.unwrap_or_else(|| "test@example.com".to_string());
crate::git_backend::configure_local_user(&repo, name, &email).unwrap();
}
["config", "user.email", email] => {
let name = repo
.config_snapshot()
.string("user.name")
.map(|value| value.to_string())
.unwrap_or_else(|| "Test User".to_string());
crate::git_backend::configure_local_user(&repo, &name, email).unwrap();
}
["add", path] => {
crate::git_backend::stage_paths(&repo, &[path.to_string()]).unwrap();
}
["commit", "-m", message] => {
crate::git_backend::commit_all(&repo, message).unwrap();
}
_ => panic!("unsupported git test helper command: {args:?}"),
}
}
#[test]
fn run_workspace_update_writes_into_selected_repo_root() -> Result<()> {
let temp_dir = TempDir::new().unwrap();
let project_root = temp_dir.path();
run_git(project_root, &["init"]);
run_git(project_root, &["config", "user.name", "Test User"]);
run_git(project_root, &["config", "user.email", "test@example.com"]);
fs::write(project_root.join("README.md"), "hello\n").unwrap();
run_git(project_root, &["add", "README.md"]);
run_git(project_root, &["commit", "-m", "initial"]);
let _workspace_state = WorkspaceState::initialize(project_root, Some("Example"))?;
let _config = StateConfig::load(project_root)?;
let db_path = project_root.join(".wsb/wsb.db");
let rt = tokio::runtime::Runtime::new().unwrap();
rt.block_on(async {
let config = crate::persist::global::DatabaseConfig::new(&db_path);
let service = crate::persist::global::DatabaseService::new(&config).unwrap();
let conn = service.get_connection().unwrap();
conn.execute(
"UPDATE projects SET major_version = 5 WHERE id = (SELECT id FROM projects LIMIT 1)",
[],
).unwrap();
});
let templates_dir = project_root.join(".wsb/templates");
fs::create_dir_all(&templates_dir).unwrap();
fs::write(templates_dir.join("version.tera"), "{{ project.version }}\n").unwrap();
fs::write(
templates_dir.join("templates.json"),
r#"{"version":{"name":"version","source_path":"version.tera","output_path":"generated/version.out","enabled":true,"description":null}}"#,
)
.unwrap();
let original_dir = std::env::current_dir().unwrap();
let unrelated_dir = TempDir::new().unwrap();
std::env::set_current_dir(unrelated_dir.path()).unwrap();
let result = run_workspace_update(project_root)?;
std::env::set_current_dir(original_dir).unwrap();
assert_eq!(result.version, "5.1.1");
assert_eq!(
fs::read_to_string(project_root.join("version.txt")).unwrap().trim(),
"5.1.1"
);
assert_eq!(
fs::read_to_string(project_root.join("generated/version.out")).unwrap().trim(),
"5.1.1"
);
Ok(())
}
}