foch 0.0.1

EU4 mod analysis and merge toolkit
Documentation
use crate::model::GamePathBuf;

#[derive(Debug, Clone)]
pub struct CandidateView {
	pub mod_id: String,
	pub mod_display_name: String,
	pub precedence: usize,
	pub change_summary: Vec<String>,
	pub candidate_rendered: String,
}

#[derive(Debug, Clone)]
pub struct ConflictView {
	/// The output file the conflict is in.
	pub file_path: GamePathBuf,
	pub address_path: Vec<String>,
	pub address_key: String,
	pub conflict_id: String,
	pub reason: String,
	pub vanilla_snippet: Option<String>,
	pub candidates: Vec<CandidateView>,
}

#[cfg(test)]
mod tests {
	use super::*;
	use crate::merge::conflict_handler::{ConflictDecision, ConflictHandler};

	struct HighestPrecedenceHandler;

	impl ConflictHandler for HighestPrecedenceHandler {
		fn on_conflict(&mut self, view: &ConflictView) -> ConflictDecision {
			view.candidates
				.iter()
				.enumerate()
				.max_by_key(|(_, candidate)| candidate.precedence)
				.map(|(candidate, _)| ConflictDecision::PickCandidate {
					candidate,
					record: None,
				})
				.unwrap_or(ConflictDecision::Defer { record: None })
		}
	}

	#[test]
	fn handler_can_decide_from_conflict_view_alone() {
		let view = ConflictView {
			file_path: GamePathBuf::parse("common/example.txt").expect("valid game path"),
			address_path: vec!["root".to_string()],
			address_key: "owner".to_string(),
			conflict_id: "abc123".to_string(),
			reason: "conflicting scalar values".to_string(),
			vanilla_snippet: Some("owner = FRA".to_string()),
			candidates: vec![
				CandidateView {
					mod_id: "mod-low".to_string(),
					mod_display_name: "Low".to_string(),
					precedence: 1,
					change_summary: vec!["set owner".to_string()],
					candidate_rendered: "owner = LOW".to_string(),
				},
				CandidateView {
					mod_id: "mod-high".to_string(),
					mod_display_name: "High".to_string(),
					precedence: 9,
					change_summary: vec!["set owner".to_string()],
					candidate_rendered: "owner = HIGH".to_string(),
				},
			],
		};

		let decision = HighestPrecedenceHandler.on_conflict(&view);

		assert_eq!(
			decision,
			ConflictDecision::PickCandidate {
				candidate: 1,
				record: None
			}
		);
	}
}