use super::*;
pub const TURN_TRACK_DEFAULT_TRIGGERS: &[&str] = &["cargo"];
pub const TURN_TRACK_DEFAULT_CACHE_DIRS: &[&str] = &["target"];
pub const TURN_TRACK_DEFAULT_SKIP_SUBDIRS: &[&str] = &["incremental"];
pub const TURN_TRACK_DEFAULT_MIN_FREE_GIB: u64 = 20;
pub const TURN_TRACK_DEFAULT_TURN_TARGET_SUBDIR: &str = "turns";
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
pub struct TurnTrackConfig {
#[serde(default)]
pub warm_build_cache: WarmBuildCacheConfig,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct WarmBuildCacheConfig {
#[serde(default = "turn_track_default_enabled")]
pub enabled: bool,
#[serde(default = "turn_track_default_triggers")]
pub triggers: Vec<String>,
#[serde(default = "turn_track_default_cache_dirs")]
pub cache_dirs: Vec<String>,
#[serde(default = "turn_track_default_skip_subdirs")]
pub skip_subdirs: Vec<String>,
#[serde(default = "turn_track_default_min_free_gib")]
pub min_free_gib: u64,
#[serde(default = "turn_track_default_turn_target_subdir")]
pub turn_target_subdir: String,
}
impl WarmBuildCacheConfig {
pub fn validated_subdir(&self) -> &str {
let mut parts = std::path::Path::new(&self.turn_target_subdir).components();
match (parts.next(), parts.next()) {
(Some(std::path::Component::Normal(_)), None) => &self.turn_target_subdir,
_ => TURN_TRACK_DEFAULT_TURN_TARGET_SUBDIR,
}
}
}
fn strings(v: &[&str]) -> Vec<String> {
v.iter().map(|s| s.to_string()).collect()
}
fn turn_track_default_enabled() -> bool {
true
}
fn turn_track_default_triggers() -> Vec<String> {
strings(TURN_TRACK_DEFAULT_TRIGGERS)
}
fn turn_track_default_cache_dirs() -> Vec<String> {
strings(TURN_TRACK_DEFAULT_CACHE_DIRS)
}
fn turn_track_default_skip_subdirs() -> Vec<String> {
strings(TURN_TRACK_DEFAULT_SKIP_SUBDIRS)
}
fn turn_track_default_min_free_gib() -> u64 {
TURN_TRACK_DEFAULT_MIN_FREE_GIB
}
fn turn_track_default_turn_target_subdir() -> String {
TURN_TRACK_DEFAULT_TURN_TARGET_SUBDIR.to_string()
}
impl Default for WarmBuildCacheConfig {
fn default() -> Self {
Self {
enabled: turn_track_default_enabled(),
triggers: turn_track_default_triggers(),
cache_dirs: turn_track_default_cache_dirs(),
skip_subdirs: turn_track_default_skip_subdirs(),
min_free_gib: turn_track_default_min_free_gib(),
turn_target_subdir: turn_track_default_turn_target_subdir(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn turn_track_defaults_when_absent() {
let c: Config = serde_yaml::from_str("{}").unwrap();
let w = &c.turn_track.warm_build_cache;
assert!(w.enabled);
assert_eq!(w.triggers, vec!["cargo".to_string()]);
assert_eq!(w.cache_dirs, vec!["target".to_string()]);
assert_eq!(w.skip_subdirs, vec!["incremental".to_string()]);
assert_eq!(w.min_free_gib, 20);
assert_eq!(w.turn_target_subdir, "turns");
}
#[test]
fn turn_track_partial_override() {
let c: Config =
serde_yaml::from_str("turn_track:\n warm_build_cache:\n min_free_gib: 5\n")
.unwrap();
let w = &c.turn_track.warm_build_cache;
assert_eq!(w.min_free_gib, 5);
assert!(w.enabled);
assert_eq!(w.triggers, vec!["cargo".to_string()]);
assert_eq!(w.cache_dirs, vec!["target".to_string()]);
assert_eq!(w.skip_subdirs, vec!["incremental".to_string()]);
assert_eq!(w.turn_target_subdir, "turns");
}
#[test]
fn turn_target_subdir_rejects_path_escape() {
let with = |s: &str| WarmBuildCacheConfig {
turn_target_subdir: s.to_string(),
..Default::default()
};
for bad in ["", "..", ".", "a/b", "/abs", "../x"] {
assert_eq!(with(bad).validated_subdir(), "turns", "input {bad:?}");
}
assert_eq!(with("x").validated_subdir(), "x");
}
}