changepacks_core/
workspace.rs

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