Add justfile summaries (#399)

Exposes an interface for producing a summary that captures the important details of the parsed justfile.
This commit is contained in:
Casey Rodarmor 2019-04-11 16:35:16 -07:00 committed by GitHub
parent e118051a5c
commit 12f9428695
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 205 additions and 9 deletions

12
Cargo.lock generated
View File

@ -127,7 +127,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "env_logger"
version = "0.5.13"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
@ -182,7 +182,7 @@ dependencies = [
[[package]]
name = "itertools"
version = "0.7.11"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"either 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
@ -200,9 +200,9 @@ dependencies = [
"ctrlc 3.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"dotenv 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)",
"edit-distance 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"env_logger 0.5.13 (registry+https://github.com/rust-lang/crates.io-index)",
"env_logger 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
"executable-path 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"itertools 0.7.11 (registry+https://github.com/rust-lang/crates.io-index)",
"itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
@ -504,14 +504,14 @@ dependencies = [
"checksum dotenv 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c0d0a1279c96732bc6800ce6337b6a614697b0e74ae058dc03c62ebeb78b4d86"
"checksum edit-distance 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bbbaaaf38131deb9ca518a274a45bfdb8771f139517b073b16c2d3d32ae5037b"
"checksum either 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "5527cfe0d098f36e3f8839852688e63c8fff1c90b2b405aef730615f9a7bcf7b"
"checksum env_logger 0.5.13 (registry+https://github.com/rust-lang/crates.io-index)" = "15b0a4d2e39f8420210be8b27eeda28029729e2fd4291019455016c348240c38"
"checksum env_logger 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b61fa891024a945da30a9581546e8cfaf5602c7b3f4c137a2805cf388f92075a"
"checksum executable-path 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3ebc5a6d89e3c90b84e8f33c8737933dda8f1c106b5415900b38b9d433841478"
"checksum failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "795bd83d3abeb9220f257e597aa0080a508b27533824adf336529648f6abf7e2"
"checksum failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "ea1063915fd7ef4309e222a5a07cf9c319fb9c7836b1f89b85458672dbb127e1"
"checksum fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba"
"checksum glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "8be18de09a56b60ed0edf84bc9df007e30040691af7acd1c41874faac5895bfb"
"checksum humantime 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3ca7e5f2e110db35f93b837c81797f3714500b81d517bf20c431b16d3ca4f114"
"checksum itertools 0.7.11 (registry+https://github.com/rust-lang/crates.io-index)" = "0d47946d458e94a1b7bcabbf6521ea7c037062c81f534615abcad76e84d4970d"
"checksum itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5b8467d9c1cebe26feb08c640139247fac215782d35371ade9a2136ed6085358"
"checksum lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bc5729f27f159ddd61f4df6228e827e86643d4d3e7c32183cb30a1c08f604a14"
"checksum libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)" = "bedcc7a809076656486ffe045abeeac163da1b558e963a31e29fbfbeba916917"
"checksum log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c84ec4b527950aa83a329754b01dbe3f58361d1c5efacd1f6d68c494d08a17c6"

View File

@ -9,15 +9,15 @@ readme = "crates-io-readme.md"
edition = "2018"
[dependencies]
ansi_term = "0.11"
ansi_term = "0.11.0"
assert_matches = "1.1.0"
atty = "0.2.1"
brev = "0.1.6"
clap = "2.0.0"
dotenv = "0.13.0"
edit-distance = "2.0.0"
env_logger = "0.5.12"
itertools = "0.7"
env_logger = "0.6.1"
itertools = "0.8.0"
lazy_static = "1.0.0"
libc = "0.2.21"
log = "0.4.4"

View File

@ -42,3 +42,5 @@ mod token;
mod verbosity;
pub use crate::run::run;
pub mod summary;

194
src/summary.rs Normal file
View File

@ -0,0 +1,194 @@
//! Justfile summary creation, for testing purposes only.
//!
//! The contents of this module are not bound by any stability guarantees.
//! Breaking changes may be introduced at any time.
//!
//! The main entry point into this module is the `summary` function, which
//! parses a justfile at a given path and produces a `Summary` object,
//! which broadly captures the functionality of the parsed justfile, or
//! an error message.
//!
//! This functionality is intended to be used with `janus`, a tool for
//! ensuring that changes to just do not inadvertently break or
//! change the interpretation of existing justfiles.
use std::{
collections::{BTreeMap, BTreeSet},
fs, io,
path::Path,
};
use crate::{expression, fragment, justfile::Justfile, parser::Parser, recipe};
pub fn summary(path: impl AsRef<Path>) -> Result<Result<Summary, String>, io::Error> {
let path = path.as_ref();
let text = fs::read_to_string(path)?;
match Parser::parse(&text) {
Ok(justfile) => Ok(Ok(Summary::new(justfile))),
Err(compilation_error) => Ok(Err(compilation_error.to_string())),
}
}
#[derive(Eq, PartialEq, Hash, Ord, PartialOrd, Debug, Clone)]
pub struct Summary {
pub assignments: BTreeMap<String, Assignment>,
pub recipes: BTreeMap<String, Recipe>,
}
impl Summary {
fn new(justfile: Justfile) -> Summary {
let exports = justfile.exports;
let mut aliases = BTreeMap::new();
for alias in justfile.aliases.values() {
aliases
.entry(alias.target)
.or_insert(Vec::new())
.push(alias.name.to_string());
}
Summary {
recipes: justfile
.recipes
.into_iter()
.map(|(name, recipe)| {
(
name.to_string(),
Recipe::new(recipe, aliases.remove(name).unwrap_or(Vec::new())),
)
})
.collect(),
assignments: justfile
.assignments
.into_iter()
.map(|(name, expression)| {
(
name.to_string(),
Assignment::new(name, expression, &exports),
)
})
.collect(),
}
}
}
#[derive(Eq, PartialEq, Hash, Ord, PartialOrd, Debug, Clone)]
pub struct Recipe {
pub aliases: Vec<String>,
pub dependencies: BTreeSet<String>,
pub lines: Vec<Line>,
pub private: bool,
pub quiet: bool,
pub shebang: bool,
}
impl Recipe {
fn new(recipe: recipe::Recipe, aliases: Vec<String>) -> Recipe {
Recipe {
private: recipe.private,
shebang: recipe.shebang,
quiet: recipe.quiet,
dependencies: recipe.dependencies.into_iter().map(str::to_owned).collect(),
lines: recipe.lines.into_iter().map(Line::new).collect(),
aliases,
}
}
}
#[derive(Eq, PartialEq, Hash, Ord, PartialOrd, Debug, Clone)]
pub struct Line {
pub fragments: Vec<Fragment>,
}
impl Line {
fn new(fragments: Vec<fragment::Fragment>) -> Line {
Line {
fragments: fragments.into_iter().map(Fragment::new).collect(),
}
}
}
#[derive(Eq, PartialEq, Hash, Ord, PartialOrd, Debug, Clone)]
pub enum Fragment {
Text { text: String },
Expression { expression: Expression },
}
impl Fragment {
fn new(fragment: fragment::Fragment) -> Fragment {
match fragment {
fragment::Fragment::Text { text } => Fragment::Text {
text: text.lexeme.to_owned(),
},
fragment::Fragment::Expression { expression } => Fragment::Expression {
expression: Expression::new(expression),
},
}
}
}
#[derive(Eq, PartialEq, Hash, Ord, PartialOrd, Debug, Clone)]
pub struct Assignment {
pub exported: bool,
pub expression: Expression,
}
impl Assignment {
fn new(name: &str, expression: expression::Expression, exports: &BTreeSet<&str>) -> Assignment {
Assignment {
exported: exports.contains(name),
expression: Expression::new(expression),
}
}
}
#[derive(Eq, PartialEq, Hash, Ord, PartialOrd, Debug, Clone)]
pub enum Expression {
Backtick {
command: String,
},
Call {
name: String,
arguments: Vec<Expression>,
},
Concatination {
lhs: Box<Expression>,
rhs: Box<Expression>,
},
String {
text: String,
},
Variable {
name: String,
},
}
impl Expression {
fn new(expression: expression::Expression) -> Expression {
use expression::Expression::*;
match expression {
Backtick { raw, .. } => Expression::Backtick {
command: raw.to_owned(),
},
Call {
name, arguments, ..
} => Expression::Call {
name: name.to_owned(),
arguments: arguments.into_iter().map(Expression::new).collect(),
},
Concatination { lhs, rhs } => Expression::Concatination {
lhs: Box::new(Expression::new(*lhs)),
rhs: Box::new(Expression::new(*rhs)),
},
String { cooked_string } => Expression::String {
text: cooked_string.cooked,
},
Variable { name, .. } => Expression::Variable {
name: name.to_owned(),
},
}
}
}