summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatthias Schiffer <mschiffer@universe-factory.net>2021-01-24 19:27:26 +0100
committerMatthias Schiffer <mschiffer@universe-factory.net>2021-01-24 19:27:26 +0100
commit9e60d16555765a6cfc053798f56e5b914ea1834e (patch)
tree0ba9b72df22a17c4dbd363d195a2f14417362c24
parent01e3de22a53d30f21a829bf5d7cebc45c4d61ea8 (diff)
downloadrebel-9e60d16555765a6cfc053798f56e5b914ea1834e.tar
rebel-9e60d16555765a6cfc053798f56e5b914ea1834e.zip
Read tasks from files
-rw-r--r--Cargo.lock206
-rw-r--r--Cargo.toml4
-rw-r--r--examples/bar.yml5
-rw-r--r--examples/foo.yml4
-rw-r--r--examples/ls.yml7
-rw-r--r--src/main.rs144
-rw-r--r--src/recipe.rs82
7 files changed, 376 insertions, 76 deletions
diff --git a/Cargo.lock b/Cargo.lock
index e64fc04..ac42b1b 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1,5 +1,211 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
[[package]]
+name = "bitmaps"
+version = "2.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "031043d04099746d8db04daf1fa424b2bc8bd69d92b25962dcde24da39ab64a2"
+dependencies = [
+ "typenum",
+]
+
+[[package]]
+name = "dtoa"
+version = "0.4.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "88d7ed2934d741c6b37e33e3832298e8850b53fd2d2bea03873375596c7cea4e"
+
+[[package]]
+name = "im"
+version = "15.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "111c1983f3c5bb72732df25cddacee9b546d08325fb584b5ebd38148be7b0246"
+dependencies = [
+ "bitmaps",
+ "rand_core",
+ "rand_xoshiro",
+ "serde",
+ "sized-chunks",
+ "typenum",
+ "version_check",
+]
+
+[[package]]
+name = "linked-hash-map"
+version = "0.5.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3"
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.24"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71"
+dependencies = [
+ "unicode-xid",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "991431c3519a3f36861882da93630ce66b52918dcf1b8e2fd66b397fc96f28df"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "rand_core"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
+
+[[package]]
+name = "rand_xoshiro"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a9fcdd2e881d02f1d9390ae47ad8e5696a9e4be7b547a1da2afbc61973217004"
+dependencies = [
+ "rand_core",
+]
+
+[[package]]
name = "rebel"
version = "0.1.0"
+dependencies = [
+ "im",
+ "serde",
+ "serde_yaml",
+ "walkdir",
+]
+
+[[package]]
+name = "same-file"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
+dependencies = [
+ "winapi-util",
+]
+
+[[package]]
+name = "serde"
+version = "1.0.121"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6159e3c76cab06f6bc466244d43b35e77e9500cd685da87620addadc2a4c40b1"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.121"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f3fcab8778dc651bc65cfab2e4eb64996f3c912b74002fb379c94517e1f27c46"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "serde_yaml"
+version = "0.8.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "971be8f6e4d4a47163b405a3df70d14359186f9ab0f3a3ec37df144ca1ce089f"
+dependencies = [
+ "dtoa",
+ "linked-hash-map",
+ "serde",
+ "yaml-rust",
+]
+
+[[package]]
+name = "sized-chunks"
+version = "0.6.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1ec31ceca5644fa6d444cc77548b88b67f46db6f7c71683b0f9336e671830d2f"
+dependencies = [
+ "bitmaps",
+ "typenum",
+]
+
+[[package]]
+name = "syn"
+version = "1.0.59"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "07cb8b1b4ebf86a89ee88cbd201b022b94138c623644d035185c84d3f41b7e66"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-xid",
+]
+
+[[package]]
+name = "typenum"
+version = "1.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33"
+
+[[package]]
+name = "unicode-xid"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
+
+[[package]]
+name = "version_check"
+version = "0.9.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed"
+
+[[package]]
+name = "walkdir"
+version = "2.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "777182bc735b6424e1a57516d35ed72cb8019d85c8c9bf536dccb3445c1a2f7d"
+dependencies = [
+ "same-file",
+ "winapi",
+ "winapi-util",
+]
+
+[[package]]
+name = "winapi"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
+dependencies = [
+ "winapi-i686-pc-windows-gnu",
+ "winapi-x86_64-pc-windows-gnu",
+]
+
+[[package]]
+name = "winapi-i686-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
+
+[[package]]
+name = "winapi-util"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
+dependencies = [
+ "winapi",
+]
+
+[[package]]
+name = "winapi-x86_64-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
+
+[[package]]
+name = "yaml-rust"
+version = "0.4.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85"
+dependencies = [
+ "linked-hash-map",
+]
diff --git a/Cargo.toml b/Cargo.toml
index 7712670..3626264 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -7,3 +7,7 @@ edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
+im = { version = "15", features = ["serde"] }
+serde = { version = "1", features = ["derive"] }
+serde_yaml = "0.8"
+walkdir = "2"
diff --git a/examples/bar.yml b/examples/bar.yml
new file mode 100644
index 0000000..08be4a5
--- /dev/null
+++ b/examples/bar.yml
@@ -0,0 +1,5 @@
+tasks:
+ build:
+ run: |
+ echo bar
+
diff --git a/examples/foo.yml b/examples/foo.yml
new file mode 100644
index 0000000..97d273a
--- /dev/null
+++ b/examples/foo.yml
@@ -0,0 +1,4 @@
+tasks:
+ build:
+ run: |
+ echo foo
diff --git a/examples/ls.yml b/examples/ls.yml
new file mode 100644
index 0000000..adc4944
--- /dev/null
+++ b/examples/ls.yml
@@ -0,0 +1,7 @@
+tasks:
+ build:
+ depends:
+ - 'foo:build'
+ - 'bar:build'
+ run: |
+ ls
diff --git a/src/main.rs b/src/main.rs
index 98d1b8d..8c5708f 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,96 +1,88 @@
-use std::collections::{HashMap, HashSet};
-// use std::io::{self, Write};
-use std::io;
-// use std::process::Command;
-
-struct Task {
- // action: String,
- deps: Vec<String>,
+use im::{HashMap, HashSet};
+use std::{fmt, path::Path, rc::Rc};
+
+mod recipe;
+
+#[derive(Debug)]
+enum Error {
+ TaskNotFound(String),
+ DependencyCycle(String),
}
-// impl Task {
-// fn run(&self) -> io::Result<()> {
-// let output = Command::new("sh").arg("-c").arg(&self.action).output()?;
-
-// let stdout = io::stdout();
-// let mut handle = stdout.lock();
-// handle.write_all(&output.stdout)?;
-
-// Ok(())
-// }
-// }
-
-fn collect_subtasks<'a>(
- queued: &HashSet<&'a str>,
- tasks: &'a HashMap<String, Task>,
- id: &str,
-) -> io::Result<HashSet<&'a str>> {
- let (task_id, task) = match tasks.get_key_value(id) {
- Some(t) => t,
- None => {
- return Err(io::Error::new(io::ErrorKind::NotFound, "Task not found"));
+impl fmt::Display for Error {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self {
+ Error::TaskNotFound(id) => write!(f, "Task not found: {}", id),
+ Error::DependencyCycle(id) => write!(f, "DependencyCycle: {}", id),
}
- };
-
- if queued.contains(id) {
- return Err(io::Error::new(
- io::ErrorKind::InvalidData,
- "Dependency cycle",
- ));
}
+}
+
+impl std::error::Error for Error {}
+
+type Result<T> = std::result::Result<T, Error>;
+
+#[derive(Default)]
+struct Context {
+ tasks: HashMap<String, Rc<recipe::Task>>,
+}
+
+impl Context {
+ fn collect_subtasks<'a>(
+ &'a self,
+ queued: &HashSet<&'a str>,
+ id: &str,
+ ) -> Result<HashSet<&'a str>> {
+ let (task_id, task) = match self.tasks.get_key_value(id) {
+ Some(t) => t,
+ None => {
+ return Err(Error::TaskNotFound(id.to_string()));
+ }
+ };
- let mut queued_sub = queued.clone();
- queued_sub.insert(task_id);
+ if queued.contains(id) {
+ return Err(Error::DependencyCycle(id.to_string()));
+ }
+
+ let queued_sub = queued.update(task_id);
- let mut subtasks: HashSet<&'a str> = HashSet::new();
- subtasks.insert(task_id);
+ let mut subtasks: HashSet<&'a str> = HashSet::new();
+ subtasks.insert(task_id);
- for dep in &task.deps {
- let deptasks = collect_subtasks(&queued_sub, tasks, dep)?;
- subtasks = subtasks.union(&deptasks).copied().collect();
+ for dep in &task.depends {
+ let deptasks = self.collect_subtasks(&queued_sub, dep)?;
+ subtasks = subtasks.union(deptasks);
+ }
+
+ Ok(subtasks)
}
- Ok(subtasks)
+ fn collect_tasks(&self, id: &str) -> Result<HashSet<&str>> {
+ self.collect_subtasks(&HashSet::new(), id)
+ }
}
-fn collect_tasks<'a>(tasks: &'a HashMap<String, Task>, id: &str) -> io::Result<HashSet<&'a str>> {
- collect_subtasks(&HashSet::new(), tasks, id)
-}
+fn main() -> Result<()> {
+ let recipes = recipe::read_recipes(Path::new("examples")).unwrap();
+
+ let mut ctx = Context::default();
+ for (recipe_name, recipe) in recipes {
+ for (task_name, task) in recipe.tasks {
+ let full_name = format!("{}:{}", recipe_name, task_name);
+ ctx.tasks.insert(full_name, Rc::new(task));
+ }
+ }
-fn main() -> io::Result<()> {
- let mut tasks = HashMap::new();
- tasks.insert(
- String::from("ls"),
- Task {
- // action: String::from("ls"),
- deps: vec!["foo".to_owned(), "bar".to_owned()],
- },
- );
- tasks.insert(
- String::from("foo"),
- Task {
- // action: String::from("echo foo"),
- deps: vec!["bar".to_owned()],
- },
- );
- tasks.insert(
- String::from("bar"),
- Task {
- // action: String::from("echo bar"),
- deps: vec![],
- },
- );
-
- let queue = collect_tasks(&tasks, "ls")?;
- let (runnable, queued) = queue
+ let queue = ctx.collect_tasks("ls:build")?;
+ let (runnable, queued): (HashSet<&str>, HashSet<&str>) = queue
.into_iter()
- .partition::<HashSet<_>, _>(|id| tasks.get(*id).unwrap().deps.is_empty());
+ .partition(|id| ctx.tasks.get(*id).unwrap().depends.is_empty());
for t in &runnable {
- println!("Runnable: {}", t);
+ println!("Runnable: {} ({:?})", t, ctx.tasks.get(*t).unwrap().run);
}
for t in &queued {
- println!("Queued: {}", t);
+ println!("Queued: {} ({:?})", t, ctx.tasks.get(*t).unwrap().run);
}
Ok(())
diff --git a/src/recipe.rs b/src/recipe.rs
new file mode 100644
index 0000000..d06b62b
--- /dev/null
+++ b/src/recipe.rs
@@ -0,0 +1,82 @@
+use im::HashMap;
+use serde::Deserialize;
+use std::{fmt, fs::File, io, path::Path};
+use walkdir::WalkDir;
+
+#[derive(Clone, Debug, Deserialize)]
+pub struct Task {
+ #[serde(default)]
+ pub depends: Vec<String>,
+ pub run: String,
+}
+
+#[derive(Clone, Debug, Deserialize)]
+pub struct Recipe {
+ pub tasks: HashMap<String, Task>,
+}
+
+#[derive(Debug)]
+pub enum Error {
+ IOError(io::Error),
+ YAMLError(serde_yaml::Error),
+}
+
+impl From<io::Error> for Error {
+ fn from(err: io::Error) -> Self {
+ Error::IOError(err)
+ }
+}
+
+impl From<serde_yaml::Error> for Error {
+ fn from(err: serde_yaml::Error) -> Self {
+ Error::YAMLError(err)
+ }
+}
+
+impl fmt::Display for Error {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self {
+ Error::IOError(err) => write!(f, "IO error: {}", err),
+ Error::YAMLError(err) => write!(f, "YAML error: {}", err),
+ }
+ }
+}
+
+impl std::error::Error for Error {}
+
+pub type Result<T> = std::result::Result<T, Error>;
+
+pub fn read_recipe(path: &Path) -> Result<Recipe> {
+ let f = File::open(path)?;
+
+ let recipe: Recipe = serde_yaml::from_reader(f)?;
+
+ Ok(recipe)
+}
+
+fn is_yml(path: &Path) -> bool {
+ path.extension() == Some("yml".as_ref())
+}
+
+pub fn read_recipes(path: &Path) -> Result<HashMap<String, Recipe>> {
+ let mut ret = HashMap::new();
+
+ for entry in WalkDir::new(path)
+ .into_iter()
+ .filter_map(|e| e.ok())
+ .filter(|e| {
+ let path = e.path();
+ path.is_file() && is_yml(path)
+ }) {
+ let path = entry.path();
+ let basename = match path.file_stem().map(|n| n.to_str()) {
+ Some(Some(v)) => v,
+ _ => continue,
+ };
+
+ let recipe = read_recipe(path)?;
+ ret.insert(basename.to_string(), recipe);
+ }
+
+ Ok(ret)
+}