changepacks_core/
package.rs

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