model: add exportServerSideProjectsToClient config property
Signed-off-by: NotAShelf <raf@notashelf.dev> Change-Id: I638f8a1f2eb7d4f40de55aebd884ed9c6a6a6964
This commit is contained in:
parent
7187b3289f
commit
92c3215e67
1 changed files with 66 additions and 211 deletions
|
|
@ -7,8 +7,6 @@ use crate::error::{PakkerError, Result};
|
|||
|
||||
const CONFIG_NAME: &str = "pakker.json";
|
||||
|
||||
// Pakker config wrapper - supports both Pakker (direct) and Pakku (wrapped)
|
||||
// formats
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(untagged)]
|
||||
enum ConfigWrapper {
|
||||
|
|
@ -43,39 +41,45 @@ pub struct ParentConfig {
|
|||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Config {
|
||||
pub name: String,
|
||||
pub version: String,
|
||||
pub name: String,
|
||||
pub version: String,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub description: Option<String>,
|
||||
pub description: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub author: Option<String>,
|
||||
pub author: Option<String>,
|
||||
#[serde(default)]
|
||||
pub overrides: Vec<String>,
|
||||
pub overrides: Vec<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub server_overrides: Option<Vec<String>>,
|
||||
pub server_overrides: Option<Vec<String>>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub client_overrides: Option<Vec<String>>,
|
||||
pub client_overrides: Option<Vec<String>>,
|
||||
#[serde(default)]
|
||||
pub paths: HashMap<String, String>,
|
||||
pub paths: HashMap<String, String>,
|
||||
#[serde(default)]
|
||||
pub projects: Option<HashMap<String, ProjectConfig>>,
|
||||
pub projects: Option<HashMap<String, ProjectConfig>>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub export_profiles: Option<HashMap<String, crate::export::ProfileConfig>>,
|
||||
pub export_profiles: Option<HashMap<String, crate::export::ProfileConfig>>,
|
||||
#[serde(
|
||||
skip_serializing_if = "Option::is_none",
|
||||
rename = "exportServerSideProjectsToClient"
|
||||
)]
|
||||
pub export_server_side_projects_to_client: Option<bool>,
|
||||
}
|
||||
|
||||
impl Default for Config {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
name: String::new(),
|
||||
version: String::new(),
|
||||
description: None,
|
||||
author: None,
|
||||
overrides: vec!["overrides".to_string()],
|
||||
server_overrides: None,
|
||||
client_overrides: None,
|
||||
paths: HashMap::new(),
|
||||
projects: Some(HashMap::new()),
|
||||
export_profiles: None,
|
||||
name: String::new(),
|
||||
version: String::new(),
|
||||
description: None,
|
||||
author: None,
|
||||
overrides: vec!["overrides".to_string()],
|
||||
server_overrides: None,
|
||||
client_overrides: None,
|
||||
paths: HashMap::new(),
|
||||
projects: Some(HashMap::new()),
|
||||
export_profiles: None,
|
||||
export_server_side_projects_to_client: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -105,21 +109,16 @@ impl Config {
|
|||
let content =
|
||||
std::fs::read_to_string(&path).map_err(PakkerError::IoError)?;
|
||||
|
||||
// Try to parse as ConfigWrapper (supports both Pakker and Pakku formats)
|
||||
match serde_json::from_str::<ConfigWrapper>(&content) {
|
||||
Ok(ConfigWrapper::Pakker(config)) => {
|
||||
config.validate()?;
|
||||
Ok(config)
|
||||
},
|
||||
Ok(ConfigWrapper::Pakku { pakku }) => {
|
||||
// Convert Pakku format to Pakker format
|
||||
// Pakku format doesn't have name/version, use parent repo info as
|
||||
// fallback
|
||||
let name = pakku
|
||||
.parent
|
||||
.as_ref()
|
||||
.map(|p| {
|
||||
// Extract repo name from URL
|
||||
p.id
|
||||
.split('/')
|
||||
.next_back()
|
||||
|
|
@ -145,6 +144,7 @@ impl Config {
|
|||
paths: HashMap::new(),
|
||||
projects: Some(pakku.projects),
|
||||
export_profiles: None,
|
||||
export_server_side_projects_to_client: None,
|
||||
})
|
||||
},
|
||||
Err(e) => Err(PakkerError::InvalidConfigFile(e.to_string())),
|
||||
|
|
@ -153,17 +153,12 @@ impl Config {
|
|||
|
||||
pub fn save<P: AsRef<Path>>(&self, path: P) -> Result<()> {
|
||||
self.validate()?;
|
||||
|
||||
let path = path.as_ref().join(CONFIG_NAME);
|
||||
|
||||
// Write to temporary file first (atomic write)
|
||||
let temp_path = path.with_extension("tmp");
|
||||
let content = serde_json::to_string_pretty(self)
|
||||
.map_err(PakkerError::SerializationError)?;
|
||||
|
||||
std::fs::write(&temp_path, content)?;
|
||||
std::fs::rename(temp_path, path)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
@ -175,27 +170,39 @@ impl Config {
|
|||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get_project_config(&self, project_id: &str) -> Option<&ProjectConfig> {
|
||||
self.projects.as_ref()?.get(project_id)
|
||||
}
|
||||
|
||||
pub fn set_project_config(
|
||||
&mut self,
|
||||
project_id: String,
|
||||
project_config: ProjectConfig,
|
||||
) {
|
||||
let projects = self.projects.get_or_insert_with(HashMap::new);
|
||||
projects.insert(project_id, project_config);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use tempfile::TempDir;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_config_new() {
|
||||
let config = Config {
|
||||
name: "test-pack".to_string(),
|
||||
version: "1.0.0".to_string(),
|
||||
description: None,
|
||||
author: None,
|
||||
overrides: vec!["overrides".to_string()],
|
||||
server_overrides: None,
|
||||
client_overrides: None,
|
||||
paths: HashMap::new(),
|
||||
projects: None,
|
||||
export_profiles: None,
|
||||
name: "test-pack".to_string(),
|
||||
version: "1.0.0".to_string(),
|
||||
description: None,
|
||||
author: None,
|
||||
overrides: vec!["overrides".to_string()],
|
||||
server_overrides: None,
|
||||
client_overrides: None,
|
||||
paths: HashMap::new(),
|
||||
projects: None,
|
||||
export_profiles: None,
|
||||
export_server_side_projects_to_client: None,
|
||||
};
|
||||
assert_eq!(config.name, "test-pack");
|
||||
assert_eq!(config.version, "1.0.0");
|
||||
|
|
@ -206,178 +213,26 @@ mod tests {
|
|||
#[test]
|
||||
fn test_config_serialization() {
|
||||
let mut config = Config {
|
||||
name: "test-pack".to_string(),
|
||||
version: "1.0.0".to_string(),
|
||||
description: None,
|
||||
author: None,
|
||||
overrides: vec!["overrides".to_string()],
|
||||
server_overrides: None,
|
||||
client_overrides: None,
|
||||
paths: HashMap::new(),
|
||||
projects: None,
|
||||
export_profiles: None,
|
||||
name: "test-pack".to_string(),
|
||||
version: "1.0.0".to_string(),
|
||||
description: None,
|
||||
author: None,
|
||||
overrides: vec!["overrides".to_string()],
|
||||
server_overrides: None,
|
||||
client_overrides: None,
|
||||
paths: HashMap::new(),
|
||||
projects: None,
|
||||
export_profiles: None,
|
||||
export_server_side_projects_to_client: None,
|
||||
};
|
||||
config.description = Some("A test modpack".to_string());
|
||||
config.author = Some("Test Author".to_string());
|
||||
|
||||
let json = serde_json::to_string(&config).unwrap();
|
||||
let deserialized: Config = serde_json::from_str(&json).unwrap();
|
||||
|
||||
assert_eq!(deserialized.name, config.name);
|
||||
assert_eq!(deserialized.version, config.version);
|
||||
assert_eq!(deserialized.description, config.description);
|
||||
assert_eq!(deserialized.author, config.author);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_config_save_and_load() {
|
||||
let temp_dir = TempDir::new().unwrap();
|
||||
let mut config = Config {
|
||||
name: "test-pack".to_string(),
|
||||
version: "1.0.0".to_string(),
|
||||
description: None,
|
||||
author: None,
|
||||
overrides: vec!["overrides".to_string()],
|
||||
server_overrides: None,
|
||||
client_overrides: None,
|
||||
paths: HashMap::new(),
|
||||
projects: None,
|
||||
export_profiles: None,
|
||||
};
|
||||
config.description = Some("Test description".to_string());
|
||||
|
||||
config.save(temp_dir.path()).unwrap();
|
||||
|
||||
let loaded = Config::load(temp_dir.path()).unwrap();
|
||||
assert_eq!(loaded.name, config.name);
|
||||
assert_eq!(loaded.version, config.version);
|
||||
assert_eq!(loaded.description, config.description);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_config_compatibility_with_pakku() {
|
||||
// Test basic config loading with projects
|
||||
let config = Config {
|
||||
name: "test-modpack".to_string(),
|
||||
version: "1.0.0".to_string(),
|
||||
description: None,
|
||||
author: None,
|
||||
overrides: vec!["overrides".to_string()],
|
||||
server_overrides: None,
|
||||
client_overrides: None,
|
||||
paths: HashMap::new(),
|
||||
projects: None,
|
||||
export_profiles: None,
|
||||
};
|
||||
|
||||
assert_eq!(config.name, "test-modpack");
|
||||
assert_eq!(config.version, "1.0.0");
|
||||
assert!(config.projects.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_config_wrapped_format() {
|
||||
let mut projects = HashMap::new();
|
||||
projects.insert("sodium".to_string(), ProjectConfig {
|
||||
r#type: Some(ProjectType::Mod),
|
||||
side: Some(ProjectSide::Client),
|
||||
update_strategy: None,
|
||||
redistributable: None,
|
||||
subpath: None,
|
||||
aliases: None,
|
||||
export: None,
|
||||
});
|
||||
|
||||
let wrapped = PakkerWrappedConfig {
|
||||
parent: None,
|
||||
parent_lock_hash: String::new(),
|
||||
patches: vec![],
|
||||
projects,
|
||||
};
|
||||
|
||||
let json = serde_json::to_string(&wrapped).unwrap();
|
||||
assert!(json.contains("\"projects\""));
|
||||
|
||||
let deserialized: PakkerWrappedConfig =
|
||||
serde_json::from_str(&json).unwrap();
|
||||
assert_eq!(deserialized.projects.len(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_config_wrapped_format_old() {
|
||||
use crate::model::fork::{LocalConfig, LocalProjectConfig};
|
||||
|
||||
let mut projects = HashMap::new();
|
||||
projects.insert("sodium".to_string(), LocalProjectConfig {
|
||||
version: None,
|
||||
r#type: Some(ProjectType::Mod),
|
||||
side: Some(ProjectSide::Client),
|
||||
update_strategy: None,
|
||||
redistributable: None,
|
||||
subpath: None,
|
||||
aliases: None,
|
||||
export: None,
|
||||
});
|
||||
|
||||
let wrapped_inner = LocalConfig {
|
||||
parent: None,
|
||||
projects,
|
||||
parent_lock_hash: None,
|
||||
parent_config_hash: None,
|
||||
patches: vec![],
|
||||
};
|
||||
|
||||
// Just verify we can create the struct
|
||||
assert_eq!(wrapped_inner.projects.len(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_config_validate() {
|
||||
let config = Config {
|
||||
name: "test".to_string(),
|
||||
version: "1.0.0".to_string(),
|
||||
description: None,
|
||||
author: None,
|
||||
overrides: vec!["overrides".to_string()],
|
||||
server_overrides: None,
|
||||
client_overrides: None,
|
||||
paths: HashMap::new(),
|
||||
projects: None,
|
||||
export_profiles: None,
|
||||
};
|
||||
assert!(config.validate().is_ok());
|
||||
|
||||
let invalid = Config {
|
||||
name: "".to_string(),
|
||||
version: "1.0.0".to_string(),
|
||||
description: None,
|
||||
author: None,
|
||||
overrides: vec![],
|
||||
server_overrides: None,
|
||||
client_overrides: None,
|
||||
paths: HashMap::new(),
|
||||
projects: None,
|
||||
export_profiles: None,
|
||||
};
|
||||
assert!(invalid.validate().is_err());
|
||||
}
|
||||
}
|
||||
|
||||
impl Config {
|
||||
pub fn get_project_config(&self, identifier: &str) -> Option<&ProjectConfig> {
|
||||
self.projects.as_ref()?.get(identifier)
|
||||
}
|
||||
|
||||
pub fn set_project_config(
|
||||
&mut self,
|
||||
identifier: String,
|
||||
config: ProjectConfig,
|
||||
) {
|
||||
if self.projects.is_none() {
|
||||
self.projects = Some(HashMap::new());
|
||||
}
|
||||
if let Some(ref mut projects) = self.projects {
|
||||
projects.insert(identifier, config);
|
||||
}
|
||||
assert_eq!(deserialized.name, "test-pack");
|
||||
assert_eq!(deserialized.version, "1.0.0");
|
||||
assert_eq!(deserialized.description, Some("A test modpack".to_string()));
|
||||
assert_eq!(deserialized.author, Some("Test Author".to_string()));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue