changepacks_core/
workspace.rs

1use std::{collections::HashSet, path::Path};
2
3use crate::{Config, Language, Package, update_type::UpdateType};
4use anyhow::{Context, Result};
5use async_trait::async_trait;
6
7#[async_trait]
8pub trait Workspace: std::fmt::Debug + Send + Sync {
9    fn name(&self) -> Option<&str>;
10    fn path(&self) -> &Path;
11    fn relative_path(&self) -> &Path;
12    fn version(&self) -> Option<&str>;
13    async fn update_version(&mut self, update_type: UpdateType) -> Result<()>;
14    fn language(&self) -> Language;
15
16    fn dependencies(&self) -> &HashSet<String>;
17    fn add_dependency(&mut self, dependency: &str);
18
19    // Default implementation for check_changed
20    fn check_changed(&mut self, path: &Path) -> Result<()> {
21        if self.is_changed() {
22            return Ok(());
23        }
24        if !path.to_string_lossy().contains(".changepacks")
25            && path.starts_with(self.path().parent().context("Parent not found")?)
26        {
27            self.set_changed(true);
28        }
29        Ok(())
30    }
31
32    fn is_changed(&self) -> bool;
33    fn set_changed(&mut self, changed: bool);
34
35    /// Get the default publish command for this workspace type
36    fn default_publish_command(&self) -> &'static str;
37
38    /// Publish the workspace using the configured command or default
39    async fn publish(&self, config: &Config) -> Result<()> {
40        let command = self.get_publish_command(config);
41        // Get the directory containing the workspace file
42        let workspace_dir = self
43            .path()
44            .parent()
45            .context("Workspace directory not found")?;
46
47        let mut cmd = if cfg!(target_os = "windows") {
48            let mut c = tokio::process::Command::new("cmd");
49            c.arg("/C");
50            c.arg(command);
51            c
52        } else {
53            let mut c = tokio::process::Command::new("sh");
54            c.arg("-c");
55            c.arg(command);
56            c
57        };
58
59        cmd.current_dir(workspace_dir);
60        let output = cmd.output().await?;
61
62        if !output.status.success() {
63            anyhow::bail!(
64                "Publish command failed: {}",
65                String::from_utf8_lossy(&output.stderr)
66            );
67        } else {
68            Ok(())
69        }
70    }
71
72    /// Get the publish command for this workspace, checking config first
73    fn get_publish_command(&self, config: &Config) -> String {
74        // Check for custom command by relative path
75        if let Some(cmd) = config
76            .publish
77            .get(self.relative_path().to_string_lossy().as_ref())
78        {
79            return cmd.clone();
80        }
81
82        // Check for custom command by language
83        let lang_key = match self.language() {
84            crate::Language::Node => "node",
85            crate::Language::Python => "python",
86            crate::Language::Rust => "rust",
87            crate::Language::Dart => "dart",
88        };
89        if let Some(cmd) = config.publish.get(lang_key) {
90            return cmd.clone();
91        }
92
93        // Use default command
94        self.default_publish_command().to_string()
95    }
96
97    async fn update_workspace_dependencies(&self, _packages: &[&dyn Package]) -> Result<()> {
98        Ok(())
99    }
100}