config.rs 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  1. use anyhow::{Context, Result};
  2. use std::collections::HashSet;
  3. use tracing::info;
  4. mod types;
  5. pub use types::*;
  6. pub fn resolve_cluster_name<'a>(clusters: &'a [Cluster], name: &str) -> Result<&'a Cluster> {
  7. if let Some(c) = clusters.iter().find(|c| c.name == name) {
  8. return Ok(c);
  9. }
  10. let matches: Vec<&Cluster> = clusters
  11. .iter()
  12. .filter(|c| c.name.starts_with(name))
  13. .collect();
  14. match matches.len() {
  15. 0 => anyhow::bail!(fluent_i18n::t!("cluster-not-found", { "name" => name.to_string() })),
  16. 1 => Ok(matches[0]),
  17. n => anyhow::bail!(
  18. "Pref '{}' matches {} clusters: {}",
  19. name,
  20. n,
  21. matches
  22. .iter()
  23. .map(|c| c.name.as_str())
  24. .collect::<Vec<_>>()
  25. .join(", ")
  26. ),
  27. }
  28. }
  29. pub fn select_cluster<'c>(
  30. config: &'c Config,
  31. cluster_name: &str,
  32. required_nodes: Option<u32>,
  33. occupied: &HashSet<String>,
  34. ) -> Result<(Cluster, &'c NcclConfig)> {
  35. if cluster_name != "auto" {
  36. let cluster = resolve_cluster_name(&config.clusters, cluster_name)?;
  37. let nccl = config
  38. .nccl
  39. .get(&cluster.name)
  40. .context(fluent_i18n::t!("nccl-config-missing"))?;
  41. return Ok((cluster.clone(), nccl));
  42. }
  43. let free = |c: &Cluster| -> u32 {
  44. c.nodes
  45. .iter()
  46. .filter(|n| !occupied.contains(&n.name))
  47. .count() as u32
  48. };
  49. let mut candidates: Vec<(&Cluster, &NcclConfig)> = config
  50. .clusters
  51. .iter()
  52. .filter_map(|c| Some((c, config.nccl.get(&c.name)?)))
  53. .filter(|(c, _)| required_nodes.is_none_or(|r| free(c) >= r))
  54. .collect();
  55. candidates.sort_by_key(|(c, _)| c.nodes.len());
  56. if let Some((cluster, nccl)) = candidates.into_iter().next() {
  57. let avail = free(cluster);
  58. info!(
  59. "{}",
  60. fluent_i18n::t!("cluster-selected", { "name" => cluster.name.to_string(), "nodes" => avail })
  61. );
  62. return Ok((cluster.clone(), nccl));
  63. }
  64. anyhow::bail!(fluent_i18n::t!("cluster-auto-fail"));
  65. }
  66. pub fn select_cluster_by_name(config: &Config, cluster_name: &str) -> Result<Cluster> {
  67. Ok(resolve_cluster_name(&config.clusters, cluster_name)?.clone())
  68. }
  69. pub fn resolve_cluster<'c>(
  70. config: &'c Config,
  71. recipe: &Recipe,
  72. cluster_name: &str,
  73. occupied: &HashSet<String>,
  74. ) -> Result<(Cluster, &'c NcclConfig)> {
  75. let choice = if cluster_name != "auto" {
  76. cluster_name.to_string()
  77. } else if let Some(ref rc) = recipe.cluster {
  78. rc.clone()
  79. } else {
  80. "auto".to_string()
  81. };
  82. select_cluster(config, &choice, recipe.required_nodes, occupied)
  83. }
  84. pub async fn resolve_mods_dir(name: &str) -> Option<std::path::PathBuf> {
  85. let cwd = std::path::PathBuf::from(format!("recipes/{name}/mods"));
  86. if cwd.is_dir() {
  87. return Some(cwd);
  88. }
  89. let cfg = default_config_dir()?
  90. .join("recipes")
  91. .join(name)
  92. .join("mods");
  93. cfg.is_dir().then_some(cfg)
  94. }
  95. pub async fn load_recipe(recipe_name: &str) -> Result<Recipe> {
  96. let cwd_path = format!("recipes/{}/recipe.toml", recipe_name);
  97. if let Ok(content) = tokio::fs::read_to_string(&cwd_path).await {
  98. return Ok(toml::from_str(&content)?);
  99. }
  100. if let Some(dir) = default_config_dir() {
  101. let in_default = dir.join("recipes").join(recipe_name).join("recipe.toml");
  102. if let Ok(content) = tokio::fs::read_to_string(&in_default).await {
  103. return Ok(toml::from_str(&content)?);
  104. }
  105. }
  106. let content = tokio::fs::read_to_string(&cwd_path).await?;
  107. Ok(toml::from_str(&content)?)
  108. }
  109. pub fn default_config_dir() -> Option<std::path::PathBuf> {
  110. dirs::config_dir().map(|p| p.join("sm121"))
  111. }
  112. #[cfg(test)]
  113. mod tests;