mirror of
https://github.com/Zenithsiz/zbuild.git
synced 2026-02-05 15:31:55 +00:00
Ast now borrows from the deserializer where possible.
This commit is contained in:
parent
c71c8a7291
commit
31442ac356
107
src/ast.rs
107
src/ast.rs
@ -9,54 +9,67 @@ use {
|
||||
/// Zbuild ast
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(serde::Deserialize)]
|
||||
pub struct Ast {
|
||||
pub struct Ast<'a> {
|
||||
/// Aliases
|
||||
#[serde(rename = "alias")]
|
||||
#[serde(default)]
|
||||
pub aliases: HashMap<String, Expr>,
|
||||
#[serde(borrow)]
|
||||
pub aliases: HashMap<String, Expr<'a>>,
|
||||
|
||||
/// Default target
|
||||
#[serde(default)]
|
||||
pub default: Vec<Target>,
|
||||
#[serde(borrow)]
|
||||
pub default: Vec<Target<'a>>,
|
||||
|
||||
/// Rules
|
||||
pub rules: HashMap<String, Rule>,
|
||||
#[serde(borrow)]
|
||||
pub rules: HashMap<String, Rule<'a>>,
|
||||
}
|
||||
|
||||
/// Output Item
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(serde::Deserialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum OutItem {
|
||||
pub enum OutItem<'a> {
|
||||
/// File
|
||||
File(Expr),
|
||||
File(#[serde(borrow)] Expr<'a>),
|
||||
|
||||
/// Dependencies file
|
||||
DepsFile { deps_file: Expr },
|
||||
DepsFile {
|
||||
#[serde(borrow)]
|
||||
deps_file: Expr<'a>,
|
||||
},
|
||||
}
|
||||
|
||||
/// Dependency Item
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(serde::Deserialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum DepItem {
|
||||
pub enum DepItem<'a> {
|
||||
/// File
|
||||
File(Expr),
|
||||
File(#[serde(borrow)] Expr<'a>),
|
||||
|
||||
/// Rule
|
||||
Rule {
|
||||
rule: Expr,
|
||||
#[serde(borrow)]
|
||||
rule: Expr<'a>,
|
||||
|
||||
#[serde(default)]
|
||||
pats: HashMap<Expr, Expr>,
|
||||
#[serde(borrow)]
|
||||
pats: HashMap<Expr<'a>, Expr<'a>>,
|
||||
},
|
||||
|
||||
/// Dependencies file
|
||||
DepsFile { deps_file: Expr },
|
||||
DepsFile {
|
||||
#[serde(borrow)]
|
||||
deps_file: Expr<'a>,
|
||||
},
|
||||
|
||||
/// Static dependency
|
||||
Static {
|
||||
#[serde(rename = "static")]
|
||||
item: StaticDepItem,
|
||||
#[serde(borrow)]
|
||||
item: StaticDepItem<'a>,
|
||||
},
|
||||
}
|
||||
|
||||
@ -64,38 +77,44 @@ pub enum DepItem {
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(serde::Deserialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum StaticDepItem {
|
||||
pub enum StaticDepItem<'a> {
|
||||
/// File
|
||||
File(Expr),
|
||||
File(#[serde(borrow)] Expr<'a>),
|
||||
|
||||
/// Dependencies file
|
||||
DepsFile { deps_file: Expr },
|
||||
DepsFile {
|
||||
#[serde(borrow)]
|
||||
deps_file: Expr<'a>,
|
||||
},
|
||||
}
|
||||
|
||||
/// Target
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(serde::Deserialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum Target {
|
||||
pub enum Target<'a> {
|
||||
/// File
|
||||
File(Expr),
|
||||
File(#[serde(borrow)] Expr<'a>),
|
||||
|
||||
/// Rule
|
||||
Rule { rule: Expr },
|
||||
Rule {
|
||||
#[serde(borrow)]
|
||||
rule: Expr<'a>,
|
||||
},
|
||||
}
|
||||
|
||||
/// Expression
|
||||
#[derive(PartialEq, Eq, Clone, Hash, Debug)]
|
||||
pub struct Expr {
|
||||
pub struct Expr<'a> {
|
||||
/// Components
|
||||
pub cmpts: Vec<ExprCmpt>,
|
||||
pub cmpts: Vec<ExprCmpt<'a>>,
|
||||
}
|
||||
|
||||
/// Expression component
|
||||
#[derive(PartialEq, Eq, Clone, Hash, Debug)]
|
||||
pub enum ExprCmpt {
|
||||
pub enum ExprCmpt<'a> {
|
||||
/// String
|
||||
String(String),
|
||||
String(Cow<'a, str>),
|
||||
|
||||
/// Pattern
|
||||
Pattern { name: String, ops: Vec<PatternOp> },
|
||||
@ -118,18 +137,20 @@ pub enum AliasOp {
|
||||
DirName,
|
||||
}
|
||||
|
||||
impl<'de> serde::Deserialize<'de> for Expr {
|
||||
impl<'a, 'de: 'a> serde::Deserialize<'de> for Expr<'a> {
|
||||
#[allow(clippy::indexing_slicing, clippy::string_slice)] // We verify the indexes are correct
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
// Parse the string
|
||||
let expr_str = Cow::<str>::deserialize(deserializer)?;
|
||||
// TODO: Use `Cow<'a, str>`? We need to clone in case we to get an owned
|
||||
// version, however, which complicates the code below
|
||||
let expr_str = <&'a str>::deserialize(deserializer)?;
|
||||
|
||||
// Then parse all components
|
||||
let mut cmpts = vec![];
|
||||
let mut rest = &*expr_str;
|
||||
let mut rest = expr_str;
|
||||
loop {
|
||||
// Try to find the next pattern / alias
|
||||
match rest.find(['$', '^']) {
|
||||
@ -137,7 +158,7 @@ impl<'de> serde::Deserialize<'de> for Expr {
|
||||
Some(idx) => {
|
||||
// Add the string until the pattern / alias, if it isn't empty
|
||||
if !rest[..idx].is_empty() {
|
||||
cmpts.push(ExprCmpt::String(rest[..idx].to_owned()));
|
||||
cmpts.push(ExprCmpt::String(Cow::Borrowed(&rest[..idx])));
|
||||
}
|
||||
|
||||
// Then check if it was an alias or pattern
|
||||
@ -202,7 +223,7 @@ impl<'de> serde::Deserialize<'de> for Expr {
|
||||
None => {
|
||||
// Add the rest only if it isn't empty
|
||||
if !rest.is_empty() {
|
||||
cmpts.push(ExprCmpt::String(rest.to_owned()));
|
||||
cmpts.push(ExprCmpt::String(Cow::Borrowed(rest)));
|
||||
}
|
||||
break;
|
||||
},
|
||||
@ -216,44 +237,49 @@ impl<'de> serde::Deserialize<'de> for Expr {
|
||||
/// Rule
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(serde::Deserialize)]
|
||||
pub struct Rule {
|
||||
pub struct Rule<'a> {
|
||||
/// Aliases
|
||||
#[serde(default)]
|
||||
pub alias: HashMap<String, Expr>,
|
||||
#[serde(borrow)]
|
||||
pub alias: HashMap<String, Expr<'a>>,
|
||||
|
||||
/// Output items
|
||||
#[serde(default)]
|
||||
pub out: Vec<OutItem>,
|
||||
#[serde(borrow)]
|
||||
pub out: Vec<OutItem<'a>>,
|
||||
|
||||
/// Dependencies
|
||||
#[serde(default)]
|
||||
pub deps: Vec<DepItem>,
|
||||
#[serde(borrow)]
|
||||
pub deps: Vec<DepItem<'a>>,
|
||||
|
||||
/// Execution
|
||||
#[serde(default)]
|
||||
pub exec: Exec,
|
||||
#[serde(borrow)]
|
||||
pub exec: Exec<'a>,
|
||||
}
|
||||
|
||||
/// Execution
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(serde::Deserialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum Exec {
|
||||
pub enum Exec<'a> {
|
||||
/// Only commands
|
||||
OnlyCmds(Vec<Command>),
|
||||
OnlyCmds(Vec<Command<'a>>),
|
||||
|
||||
/// Full
|
||||
Full {
|
||||
/// working directory
|
||||
#[serde(default)]
|
||||
cwd: Option<Expr>,
|
||||
#[serde(borrow)]
|
||||
cwd: Option<Expr<'a>>,
|
||||
|
||||
/// Commands
|
||||
cmds: Vec<Command>,
|
||||
cmds: Vec<Command<'a>>,
|
||||
},
|
||||
}
|
||||
|
||||
impl Default for Exec {
|
||||
impl<'a> Default for Exec<'a> {
|
||||
fn default() -> Self {
|
||||
Self::OnlyCmds(vec![])
|
||||
}
|
||||
@ -264,7 +290,8 @@ impl Default for Exec {
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(serde::Deserialize)]
|
||||
#[serde(transparent)]
|
||||
pub struct Command {
|
||||
pub struct Command<'a> {
|
||||
/// All arguments
|
||||
pub args: Vec<Expr>,
|
||||
#[serde(borrow)]
|
||||
pub args: Vec<Expr<'a>>,
|
||||
}
|
||||
|
||||
22
src/build.rs
22
src/build.rs
@ -25,7 +25,6 @@ use {
|
||||
std::{
|
||||
collections::HashMap,
|
||||
mem,
|
||||
path::Path,
|
||||
time::{Duration, SystemTime},
|
||||
},
|
||||
tokio::{fs, process::Command, sync::Semaphore},
|
||||
@ -263,14 +262,18 @@ impl Builder {
|
||||
is_static,
|
||||
is_dep_file: false,
|
||||
is_output: false,
|
||||
exists: fs_try_exists(file).await.map_err(AppError::check_file_exists(file))?,
|
||||
exists: util::fs_try_exists(file)
|
||||
.await
|
||||
.map_err(AppError::check_file_exists(file))?,
|
||||
}),
|
||||
DepItem::DepsFile { ref file, is_static } => Ok(Dep::File {
|
||||
file,
|
||||
is_static,
|
||||
is_dep_file: true,
|
||||
is_output: false,
|
||||
exists: fs_try_exists(file).await.map_err(AppError::check_file_exists(file))?,
|
||||
exists: util::fs_try_exists(file)
|
||||
.await
|
||||
.map_err(AppError::check_file_exists(file))?,
|
||||
}),
|
||||
DepItem::Rule { ref name, ref pats } => Ok(Dep::Rule { name, pats }),
|
||||
})
|
||||
@ -289,7 +292,9 @@ impl Builder {
|
||||
is_static: false,
|
||||
is_dep_file: true,
|
||||
is_output: true,
|
||||
exists: fs_try_exists(file).await.map_err(AppError::check_file_exists(file))?,
|
||||
exists: util::fs_try_exists(file)
|
||||
.await
|
||||
.map_err(AppError::check_file_exists(file))?,
|
||||
})),
|
||||
})
|
||||
.collect::<FuturesUnordered<_>>()
|
||||
@ -613,12 +618,3 @@ fn file_modified_time(metadata: std::fs::Metadata) -> SystemTime {
|
||||
|
||||
SystemTime::UNIX_EPOCH + unix_offset
|
||||
}
|
||||
|
||||
/// Async `std::fs_try_exists`
|
||||
async fn fs_try_exists(path: impl AsRef<Path> + Send) -> Result<bool, std::io::Error> {
|
||||
match fs::metadata(path).await {
|
||||
Ok(_) => Ok(true),
|
||||
Err(err) if err.kind() == std::io::ErrorKind::NotFound => Ok(false),
|
||||
Err(err) => Err(err),
|
||||
}
|
||||
}
|
||||
|
||||
18
src/error.rs
18
src/error.rs
@ -43,17 +43,6 @@ pub enum AppError {
|
||||
err: io::Error,
|
||||
},
|
||||
|
||||
/// Open file
|
||||
#[error("Unable to open file {file_path:?}")]
|
||||
OpenFile {
|
||||
/// File we failed to open
|
||||
file_path: PathBuf,
|
||||
|
||||
/// Underlying error
|
||||
#[source]
|
||||
err: io::Error,
|
||||
},
|
||||
|
||||
/// Read file
|
||||
#[error("Unable to read file {file_path:?}")]
|
||||
ReadFile {
|
||||
@ -339,13 +328,6 @@ impl AppError {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn open_file(file_path: impl Into<PathBuf>) -> impl FnOnce(io::Error) -> Self {
|
||||
move |err| Self::OpenFile {
|
||||
file_path: file_path.into(),
|
||||
err,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read_file(file_path: impl Into<PathBuf>) -> impl FnOnce(io::Error) -> Self {
|
||||
move |err| Self::ReadFile {
|
||||
file_path: file_path.into(),
|
||||
|
||||
22
src/main.rs
22
src/main.rs
@ -80,7 +80,8 @@ use {
|
||||
args::Args,
|
||||
clap::StructOpt,
|
||||
futures::{stream::FuturesUnordered, TryStreamExt},
|
||||
std::{collections::HashMap, env, fs, path::PathBuf},
|
||||
std::{collections::HashMap, env, path::PathBuf},
|
||||
tokio::fs,
|
||||
watcher::Watcher,
|
||||
};
|
||||
|
||||
@ -99,18 +100,18 @@ async fn main() -> Result<(), anyhow::Error> {
|
||||
// Find the zbuild location and change the current directory to it
|
||||
let zbuild_path = match args.zbuild_path {
|
||||
Some(path) => path,
|
||||
None => self::find_zbuild()?,
|
||||
None => self::find_zbuild().await?,
|
||||
};
|
||||
let zbuild_dir = zbuild_path.parent().expect("Zbuild path had no parent");
|
||||
tracing::trace!(?zbuild_path, "Found zbuild path");
|
||||
std::env::set_current_dir(zbuild_dir).map_err(AppError::set_current_dir(zbuild_dir))?;
|
||||
|
||||
// Parse the ast
|
||||
let ast = {
|
||||
let zbuild_file = fs::File::open(&zbuild_path).map_err(AppError::open_file(&zbuild_path))?;
|
||||
serde_yaml::from_reader::<_, Ast>(zbuild_file).map_err(AppError::parse_yaml(&zbuild_path))?
|
||||
};
|
||||
tracing::trace!(target: "zbuild_ast", ?ast, "Parsed ast");
|
||||
let zbuild_file = fs::read_to_string(&zbuild_path)
|
||||
.await
|
||||
.map_err(AppError::read_file(&zbuild_path))?;
|
||||
let ast = serde_yaml::from_str::<Ast>(&zbuild_file).map_err(AppError::parse_yaml(&zbuild_path))?;
|
||||
tracing::trace!(target: "zbuild_ast", "Parsed ast: {ast:#?}");
|
||||
|
||||
// Build the rules
|
||||
let rules = Rules::new(ast);
|
||||
@ -203,13 +204,16 @@ async fn main() -> Result<(), anyhow::Error> {
|
||||
}
|
||||
|
||||
/// Finds the nearest zbuild file
|
||||
fn find_zbuild() -> Result<PathBuf, AppError> {
|
||||
async fn find_zbuild() -> Result<PathBuf, AppError> {
|
||||
let cur_path = env::current_dir().map_err(AppError::get_current_dir())?;
|
||||
let mut cur_path = cur_path.as_path();
|
||||
|
||||
loop {
|
||||
let zbuild_path = cur_path.join("zbuild.yaml");
|
||||
match fs::try_exists(&zbuild_path).map_err(AppError::check_file_exists(&zbuild_path))? {
|
||||
match util::fs_try_exists(&zbuild_path)
|
||||
.await
|
||||
.map_err(AppError::check_file_exists(&zbuild_path))?
|
||||
{
|
||||
true => return Ok(zbuild_path),
|
||||
false => match cur_path.parent() {
|
||||
Some(parent) => cur_path = parent,
|
||||
|
||||
@ -37,7 +37,7 @@ impl Expr {
|
||||
.cmpts
|
||||
.into_iter()
|
||||
.map(|cmpt| match cmpt {
|
||||
ast::ExprCmpt::String(s) => ExprCmpt::String(s),
|
||||
ast::ExprCmpt::String(s) => ExprCmpt::String(s.into_owned()),
|
||||
ast::ExprCmpt::Pattern { name, ops } => ExprCmpt::Pattern(Pattern {
|
||||
name,
|
||||
ops: ops
|
||||
|
||||
12
src/util.rs
12
src/util.rs
@ -1,5 +1,8 @@
|
||||
//! Utilities
|
||||
|
||||
// Imports
|
||||
use {std::path::Path, tokio::fs};
|
||||
|
||||
/// Chains together any number of `IntoIterator`s
|
||||
pub macro chain {
|
||||
($lhs:expr, $rhs:expr $(,)?) => {
|
||||
@ -10,3 +13,12 @@ pub macro chain {
|
||||
::std::iter::Iterator::chain($lhs.into_iter(), $crate::util::chain!($rhs, $($rest),*))
|
||||
},
|
||||
}
|
||||
|
||||
/// Async `std::fs_try_exists`
|
||||
pub async fn fs_try_exists(path: impl AsRef<Path> + Send) -> Result<bool, std::io::Error> {
|
||||
match fs::metadata(path).await {
|
||||
Ok(_) => Ok(true),
|
||||
Err(err) if err.kind() == std::io::ErrorKind::NotFound => Ok(false),
|
||||
Err(err) => Err(err),
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user