Skip to main content

smbcloud_model/
runner.rs

1use {
2    crate::error_codes::{ErrorCode::UnsupportedRunner, ErrorResponse},
3    serde::{Deserialize, Serialize},
4    serde_repr::{Deserialize_repr, Serialize_repr},
5    std::{
6        fmt::{self, Display, Formatter},
7        path::PathBuf,
8    },
9};
10
11#[derive(Debug, Deserialize_repr, Serialize_repr, Clone, Copy, PartialEq, Default)]
12#[repr(u8)]
13#[tsync::tsync]
14pub enum Runner {
15    #[default]
16    NodeJs = 0,
17    /// A pure static site: no app process on the server, nginx serves files
18    /// directly. Always deployed via rsync — git push has no build step to run.
19    Static = 1,
20    Ruby = 2,
21    Swift = 3,
22    Monorepo = 255,
23}
24
25impl Display for Runner {
26    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
27        match self {
28            Runner::NodeJs => write!(f, "NodeJs"),
29            Runner::Static => write!(f, "Static"),
30            Runner::Ruby => write!(f, "Ruby"),
31            Runner::Swift => write!(f, "Swift"),
32            Runner::Monorepo => write!(f, "Monorepo"),
33        }
34    }
35}
36
37#[derive(Debug, Serialize, Deserialize)]
38#[tsync::tsync]
39pub enum NodeJsFramework {
40    NextJs,
41    Astro,
42}
43
44#[derive(Debug, Serialize, Deserialize)]
45#[tsync::tsync]
46pub enum RubyFramework {
47    Rails,
48}
49
50#[derive(Debug, Serialize, Deserialize)]
51#[tsync::tsync]
52pub enum SwiftFramework {
53    Vapor,
54}
55
56impl Runner {
57    pub fn from(repo_path: &PathBuf) -> Result<Runner, ErrorResponse> {
58        // Any package.json-driven app belongs on the NodeJs runner.
59        // Framework-specific checks are not reliable because modern Next.js apps
60        // do not need a next.config.* file at all.
61        if repo_path.join("package.json").exists() {
62            return Ok(Runner::NodeJs);
63        }
64
65        if repo_path.join("Gemfile").exists() {
66            return Ok(Runner::Ruby);
67        }
68        if repo_path.join("Package.swift").exists() {
69            return Ok(Runner::Swift);
70        }
71        // See if we have a monorepo setup.
72        non_framework_runner()
73    }
74
75    pub fn git_host(&self) -> String {
76        format!("git@{}.smbcloud.xyz", self.api())
77    }
78
79    /// Returns the explicit hostname used for rsync SSH connections.
80    /// e.g. `api.smbcloud.xyz` or `api-1.smbcloud.xyz`
81    pub fn rsync_host(&self) -> String {
82        format!("{}.smbcloud.xyz", self.api())
83    }
84
85    fn api(&self) -> &str {
86        match self {
87            Runner::Monorepo => "monorepo",
88            // Static sites and NodeJs projects share the same lightweight tier
89            Runner::NodeJs | Runner::Static => "api",
90            Runner::Ruby | Runner::Swift => "api-1",
91        }
92    }
93}
94
95fn non_framework_runner() -> Result<Runner, ErrorResponse> {
96    Err(ErrorResponse::Error {
97        error_code: UnsupportedRunner,
98        message: UnsupportedRunner.message(None).to_string(),
99    })
100}