summaryrefslogtreecommitdiffstats
path: root/src/resolve.rs
blob: f3ad284064d96b9798888fba9852a586cabc86f1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
use std::collections::{HashMap, HashSet};
use std::fmt;

use crate::types::*;

#[derive(Debug)]
pub struct DepChain(pub Vec<TaskRef>);

impl fmt::Display for DepChain {
	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
		let mut first = true;
		for task in self.0.iter().rev() {
			if !first {
				write!(f, " -> ")?;
			}
			write!(f, "{}", task)?;

			first = false;
		}

		Ok(())
	}
}

#[derive(Debug)]
pub enum Error {
	TaskNotFound(DepChain),
	DependencyCycle(DepChain),
}

impl Error {
	fn extend(&mut self, task: TaskRef) {
		let tasks = match self {
			Error::TaskNotFound(ref mut tasks) => tasks,
			Error::DependencyCycle(ref mut tasks) => tasks,
		};
		tasks.0.push(task);
	}
}

impl fmt::Display for Error {
	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
		match self {
			Error::TaskNotFound(tasks) => {
				write!(f, "Task not found: {}", tasks)
			}
			Error::DependencyCycle(tasks) => {
				write!(f, "Dependency Cycle: {}", tasks)
			}
		}
	}
}

impl std::error::Error for Error {}

#[derive(Debug, PartialEq)]
enum ResolveState {
	Resolving,
	Resolved,
}

#[derive(Debug)]
pub struct Resolver<'a> {
	tasks: &'a TaskMap,
	resolve_state: HashMap<TaskRef, ResolveState>,
}

impl<'a> Resolver<'a> {
	pub fn new(tasks: &'a TaskMap) -> Self {
		Resolver {
			tasks,
			resolve_state: HashMap::new(),
		}
	}

	fn tasks_resolved(&self) -> bool {
		self.resolve_state
			.values()
			.all(|resolved| *resolved == ResolveState::Resolved)
	}

	pub fn add_task(&mut self, task: &TaskRef) -> Vec<Error> {
		match self.resolve_state.get(task) {
			Some(ResolveState::Resolving) => {
				return vec![Error::DependencyCycle(DepChain(vec![task.clone()]))]
			}
			Some(ResolveState::Resolved) => return vec![],
			None => (),
		}

		let task_def = match self.tasks.get(task) {
			None => return vec![Error::TaskNotFound(DepChain(vec![task.clone()]))],
			Some(task_def) => task_def,
		};

		self.resolve_state
			.insert(task.clone(), ResolveState::Resolving);

		let mut ret = Vec::new();

		for dep in &task_def.depends {
			let errors = self.add_task(dep);
			for mut error in errors {
				error.extend(task.clone());
				ret.push(error);
			}
		}

		if ret.is_empty() {
			*self
				.resolve_state
				.get_mut(task)
				.expect("Missing resolve_state") = ResolveState::Resolved;
		} else {
			self.resolve_state.remove(task);
		}

		ret
	}

	pub fn add_goal(&mut self, task: &TaskRef) -> Vec<Error> {
		let ret = self.add_task(task);
		debug_assert!(self.tasks_resolved());
		ret
	}

	pub fn to_taskset(self) -> HashSet<TaskRef> {
		debug_assert!(self.tasks_resolved());

		self.resolve_state
			.into_iter()
			.map(|entry| entry.0)
			.collect()
	}
}