mirror of
https://github.com/Zenithsiz/zbuild.git
synced 2026-02-03 22:23:53 +00:00
Replaced CowStr with ArcStr.
This is a reference-counted string with efficient slicing, cloning and conversion to/from `String`.
This commit is contained in:
parent
296136f0fd
commit
00ad444194
@ -31,10 +31,8 @@ tracing-subscriber = { version = "0.3.18", features = ["env-filter"] }
|
||||
|
||||
[lints]
|
||||
|
||||
# This project doesn't require unsafe code
|
||||
# Note: Although we don't use unsafe code, if we do in the future, `unsafe_op_in_unsafe_fn`
|
||||
# should be denied, so we deny it already, despite it never triggering due to forbidding `rust.unsafe_code`
|
||||
rust.unsafe_code = "forbid"
|
||||
# This project doesn't require unsafe code (outside of some modules)
|
||||
rust.unsafe_code = "deny"
|
||||
rust.unsafe_op_in_unsafe_fn = "deny"
|
||||
|
||||
# Group lints
|
||||
|
||||
83
src/build.rs
83
src/build.rs
@ -17,7 +17,7 @@ use {
|
||||
error::ResultMultiple,
|
||||
expand,
|
||||
rules::{Command, CommandArg, DepItem, Expr, OutItem, Rule, Target},
|
||||
util::{self, CowStr},
|
||||
util::{self, ArcStr},
|
||||
AppError,
|
||||
Expander,
|
||||
Rules,
|
||||
@ -36,10 +36,10 @@ pub enum Event {
|
||||
/// Target dependency built
|
||||
TargetDepBuilt {
|
||||
/// Target that was being built
|
||||
target: Target<CowStr>,
|
||||
target: Target<ArcStr>,
|
||||
|
||||
/// Dependency that was built
|
||||
dep: Target<CowStr>,
|
||||
dep: Target<ArcStr>,
|
||||
},
|
||||
}
|
||||
|
||||
@ -47,10 +47,10 @@ pub enum Event {
|
||||
#[derive(PartialEq, Eq, Clone, Hash, Debug)]
|
||||
pub struct TargetRule {
|
||||
/// Name
|
||||
name: &'static str,
|
||||
name: ArcStr,
|
||||
|
||||
/// Patterns
|
||||
pats: Arc<BTreeMap<CowStr, CowStr>>,
|
||||
pats: Arc<BTreeMap<ArcStr, ArcStr>>,
|
||||
}
|
||||
|
||||
/// Builder
|
||||
@ -95,7 +95,7 @@ impl Builder {
|
||||
}
|
||||
|
||||
/// Returns all build results
|
||||
pub fn into_build_results(self) -> IndexMap<&'static str, Option<Result<BuildResult, ()>>> {
|
||||
pub fn into_build_results(self) -> IndexMap<ArcStr, Option<Result<BuildResult, ()>>> {
|
||||
self.rules_lock
|
||||
.into_iter()
|
||||
.map(|(rule, lock)| (rule.name, lock.into_res()))
|
||||
@ -124,15 +124,18 @@ impl Builder {
|
||||
/// Finds a target's rule
|
||||
fn target_rule(
|
||||
&self,
|
||||
target: &Target<CowStr>,
|
||||
target: &Target<ArcStr>,
|
||||
rules: &Rules,
|
||||
) -> Result<Option<(Rule<CowStr>, TargetRule)>, AppError> {
|
||||
) -> Result<Option<(Rule<ArcStr>, TargetRule)>, AppError> {
|
||||
let target_rule = match *target {
|
||||
// If we got a file, check which rule can make it
|
||||
Target::File { ref file, .. } => match self.find_rule_for_file(file, rules)? {
|
||||
Some((rule, pats)) => {
|
||||
tracing::trace!(%target, %rule.name, "Found target rule");
|
||||
let target_rule = TargetRule { name: rule.name, pats };
|
||||
let target_rule = TargetRule {
|
||||
name: rule.name.clone(),
|
||||
pats,
|
||||
};
|
||||
(rule, target_rule)
|
||||
},
|
||||
|
||||
@ -149,9 +152,9 @@ impl Builder {
|
||||
let rule = self
|
||||
.expander
|
||||
.expand_rule(rule, &expand_visitor)
|
||||
.map_err(AppError::expand_rule(rule.name))?;
|
||||
.map_err(AppError::expand_rule(&*rule.name))?;
|
||||
let target_rule = TargetRule {
|
||||
name: rule.name,
|
||||
name: rule.name.clone(),
|
||||
pats: Arc::clone(pats),
|
||||
};
|
||||
(rule, target_rule)
|
||||
@ -162,7 +165,7 @@ impl Builder {
|
||||
}
|
||||
|
||||
/// Resets a build
|
||||
pub async fn reset_build(&self, target: &Target<CowStr>, rules: &Rules) -> Result<(), AppError> {
|
||||
pub async fn reset_build(&self, target: &Target<ArcStr>, rules: &Rules) -> Result<(), AppError> {
|
||||
// Get the rule for the target
|
||||
let Some((_, target_rule)) = self.target_rule(target, rules)? else {
|
||||
return Ok(());
|
||||
@ -204,7 +207,7 @@ impl Builder {
|
||||
/// Builds a target
|
||||
pub async fn build(
|
||||
&self,
|
||||
target: &Target<CowStr>,
|
||||
target: &Target<ArcStr>,
|
||||
rules: &Rules,
|
||||
ignore_missing: bool,
|
||||
reason: BuildReason<'_>,
|
||||
@ -214,7 +217,7 @@ impl Builder {
|
||||
// Normalize file paths
|
||||
let target = match *target {
|
||||
Target::File { ref file, is_static } => Target::File {
|
||||
file: CowStr::Owned(util::normalize_path(file)),
|
||||
file: util::normalize_path(file).into(),
|
||||
is_static,
|
||||
},
|
||||
ref target @ Target::Rule { .. } => target.clone(),
|
||||
@ -330,8 +333,8 @@ impl Builder {
|
||||
#[async_recursion::async_recursion]
|
||||
async fn build_unchecked<'reason>(
|
||||
&self,
|
||||
target: &Target<CowStr>,
|
||||
rule: &Rule<CowStr>,
|
||||
target: &Target<ArcStr>,
|
||||
rule: &Rule<ArcStr>,
|
||||
rules: &Rules,
|
||||
ignore_missing: bool,
|
||||
reason: BuildReason<'reason>,
|
||||
@ -344,7 +347,7 @@ impl Builder {
|
||||
enum Dep {
|
||||
/// File
|
||||
File {
|
||||
file: CowStr,
|
||||
file: ArcStr,
|
||||
is_static: bool,
|
||||
is_deps_file: bool,
|
||||
is_output: bool,
|
||||
@ -354,8 +357,8 @@ impl Builder {
|
||||
|
||||
/// Rule
|
||||
Rule {
|
||||
name: CowStr,
|
||||
pats: Arc<BTreeMap<CowStr, CowStr>>,
|
||||
name: ArcStr,
|
||||
pats: Arc<BTreeMap<ArcStr, ArcStr>>,
|
||||
},
|
||||
}
|
||||
|
||||
@ -554,7 +557,9 @@ impl Builder {
|
||||
// Then rebuild, if needed
|
||||
if needs_rebuilt {
|
||||
tracing::trace!(%target, ?rule.name, ?deps_last_build_time, ?rule_last_build_time, "Rebuilding target rule");
|
||||
self.rebuild_rule(rule).await.map_err(AppError::build_rule(rule.name))?;
|
||||
self.rebuild_rule(rule)
|
||||
.await
|
||||
.map_err(AppError::build_rule(&*rule.name))?;
|
||||
}
|
||||
|
||||
// Then get the build time
|
||||
@ -573,13 +578,13 @@ impl Builder {
|
||||
/// Returns the latest modification date of the dependencies
|
||||
async fn build_deps_file(
|
||||
&self,
|
||||
parent_target: &Target<CowStr>,
|
||||
parent_target: &Target<ArcStr>,
|
||||
deps_file: &str,
|
||||
rule: &Rule<CowStr>,
|
||||
rule: &Rule<ArcStr>,
|
||||
rules: &Rules,
|
||||
ignore_missing: bool,
|
||||
reason: BuildReason<'_>,
|
||||
) -> Result<Vec<(Target<CowStr>, BuildResult, Option<BuildLockDepGuard>)>, AppError> {
|
||||
) -> Result<Vec<(Target<ArcStr>, BuildResult, Option<BuildLockDepGuard>)>, AppError> {
|
||||
tracing::trace!(target=?parent_target, ?rule.name, ?deps_file, "Building dependencies of target rule dependency-file");
|
||||
let (output, deps) = self::parse_deps_file(deps_file).await?;
|
||||
|
||||
@ -587,10 +592,10 @@ impl Builder {
|
||||
// If there were no outputs, make sure it matches the rule name
|
||||
// TODO: Seems kinda weird for it to match the rule name, but not sure how else to check this here
|
||||
true =>
|
||||
if output != rule.name {
|
||||
if output != *rule.name {
|
||||
return Err(AppError::DepFileMissingRuleName {
|
||||
deps_file_path: deps_file.into(),
|
||||
rule_name: rule.name.to_owned(),
|
||||
rule_name: rule.name.to_string(),
|
||||
dep_output: output,
|
||||
});
|
||||
},
|
||||
@ -598,7 +603,7 @@ impl Builder {
|
||||
// If there were any output, make sure the dependency file applies to one of them
|
||||
false => {
|
||||
let any_matches = rule.output.iter().any(|out| match out {
|
||||
OutItem::File { file, .. } => file == &output,
|
||||
OutItem::File { file, .. } => **file == output,
|
||||
});
|
||||
if !any_matches {
|
||||
return Err(AppError::DepFileMissingOutputs {
|
||||
@ -615,7 +620,7 @@ impl Builder {
|
||||
let deps_res = deps
|
||||
.into_iter()
|
||||
.map(|dep| {
|
||||
let dep = CowStr::Owned(util::normalize_path(&dep));
|
||||
let dep = ArcStr::from(util::normalize_path(&dep));
|
||||
tracing::trace!(?rule.name, ?dep, "Found rule dependency");
|
||||
let dep_target = Target::File {
|
||||
file: dep,
|
||||
@ -646,7 +651,7 @@ impl Builder {
|
||||
}
|
||||
|
||||
/// Rebuilds a rule
|
||||
pub async fn rebuild_rule(&self, rule: &Rule<CowStr>) -> Result<(), AppError> {
|
||||
pub async fn rebuild_rule(&self, rule: &Rule<ArcStr>) -> Result<(), AppError> {
|
||||
// Lock the semaphore
|
||||
// Note: If we locked it per-command, we could exit earlier
|
||||
// when closed, but that would break some executions.
|
||||
@ -660,7 +665,7 @@ impl Builder {
|
||||
};
|
||||
|
||||
for cmd in &rule.exec.cmds {
|
||||
self.exec_cmd(rule.name, cmd).await?;
|
||||
self.exec_cmd(&rule.name, cmd).await?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@ -670,7 +675,7 @@ impl Builder {
|
||||
#[expect(unused_results, reason = "Due to the builder pattern of `Command`")]
|
||||
#[expect(clippy::unused_self, reason = "It might be used in the future")]
|
||||
#[async_recursion::async_recursion]
|
||||
async fn exec_cmd(&self, rule_name: &str, cmd: &Command<CowStr>) -> Result<(), AppError> {
|
||||
async fn exec_cmd(&self, rule_name: &str, cmd: &Command<ArcStr>) -> Result<(), AppError> {
|
||||
// Process all arguments
|
||||
let args = cmd
|
||||
.args
|
||||
@ -729,9 +734,9 @@ impl Builder {
|
||||
#[expect(clippy::type_complexity, reason = "TODO: Add some type aliases / struct")]
|
||||
pub fn find_rule_for_file(
|
||||
&self,
|
||||
file: &str,
|
||||
file: &ArcStr,
|
||||
rules: &Rules,
|
||||
) -> Result<Option<(Rule<CowStr>, Arc<BTreeMap<CowStr, CowStr>>)>, AppError> {
|
||||
) -> Result<Option<(Rule<ArcStr>, Arc<BTreeMap<ArcStr, ArcStr>>)>, AppError> {
|
||||
for rule in rules.rules.values() {
|
||||
for output in &rule.output {
|
||||
// Expand all expressions in the output file
|
||||
@ -744,14 +749,14 @@ impl Builder {
|
||||
let output_file = self.expander.expand_expr(output_file, &expand_visitor)?;
|
||||
|
||||
// Then try to match the output file to the file we need to create
|
||||
if let Some(rule_pats) = self::match_expr(&output_file, &output_file.cmpts, file)? {
|
||||
if let Some(rule_pats) = self::match_expr(&output_file, &output_file.cmpts, file.clone())? {
|
||||
let rule_pats = Arc::new(rule_pats);
|
||||
|
||||
let expand_visitor = expand::Visitor::new([&rule.aliases, &rules.aliases], [&rule_pats]);
|
||||
let rule = self
|
||||
.expander
|
||||
.expand_rule(rule, &expand_visitor)
|
||||
.map_err(AppError::expand_rule(rule.name.to_owned()))?;
|
||||
.map_err(AppError::expand_rule(&*rule.name))?;
|
||||
return Ok(Some((rule, rule_pats)));
|
||||
}
|
||||
}
|
||||
@ -782,7 +787,7 @@ async fn parse_deps_file(file: &str) -> Result<(String, Vec<String>), AppError>
|
||||
///
|
||||
/// Returns `Err` if any files didn't exist,
|
||||
/// Returns `Ok(None)` if rule has no outputs
|
||||
async fn rule_last_build_time(rule: &Rule<CowStr>) -> Result<Option<SystemTime>, AppError> {
|
||||
async fn rule_last_build_time(rule: &Rule<ArcStr>) -> Result<Option<SystemTime>, AppError> {
|
||||
// Note: We get the time of the oldest file in order to ensure all
|
||||
// files are at-least that old
|
||||
let built_time = rule
|
||||
@ -813,7 +818,7 @@ async fn rule_last_build_time(rule: &Rule<CowStr>) -> Result<Option<SystemTime>,
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct BuildReasonInner<'a> {
|
||||
/// Target
|
||||
target: &'a Target<CowStr>,
|
||||
target: &'a Target<ArcStr>,
|
||||
|
||||
/// Previous reason
|
||||
prev: &'a BuildReason<'a>,
|
||||
@ -830,14 +835,14 @@ impl BuildReason<'_> {
|
||||
}
|
||||
|
||||
/// Adds a target to this build reason
|
||||
pub const fn with_target<'a>(&'a self, target: &'a Target<CowStr>) -> BuildReason<'a> {
|
||||
pub const fn with_target<'a>(&'a self, target: &'a Target<ArcStr>) -> BuildReason<'a> {
|
||||
BuildReason(Some(BuildReasonInner { target, prev: self }))
|
||||
}
|
||||
|
||||
/// Iterates over all reasons
|
||||
pub fn for_each<F, R>(&self, mut f: F) -> R
|
||||
where
|
||||
F: FnMut(&Target<CowStr>) -> R,
|
||||
F: FnMut(&Target<ArcStr>) -> R,
|
||||
R: Try<Output = ()>,
|
||||
{
|
||||
let mut reason = &self.0;
|
||||
@ -850,7 +855,7 @@ impl BuildReason<'_> {
|
||||
}
|
||||
|
||||
/// Collects all reasons
|
||||
pub fn collect_all(&self) -> Vec<&Target<CowStr>> {
|
||||
pub fn collect_all(&self) -> Vec<&Target<ArcStr>> {
|
||||
let mut targets = vec![];
|
||||
|
||||
let mut reason = &self.0;
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
use {
|
||||
crate::{
|
||||
rules::{Expr, ExprCmpt, PatternOp},
|
||||
util::CowStr,
|
||||
util::ArcStr,
|
||||
AppError,
|
||||
},
|
||||
std::{assert_matches::assert_matches, collections::BTreeMap},
|
||||
@ -18,8 +18,8 @@ use {
|
||||
pub fn match_expr(
|
||||
expr: &Expr,
|
||||
cmpts: &[ExprCmpt],
|
||||
mut value: &str,
|
||||
) -> Result<Option<BTreeMap<CowStr, CowStr>>, AppError> {
|
||||
mut value: ArcStr,
|
||||
) -> Result<Option<BTreeMap<ArcStr, ArcStr>>, AppError> {
|
||||
let mut patterns = BTreeMap::new();
|
||||
|
||||
// Until `rhs` has anything to match left
|
||||
@ -65,10 +65,10 @@ pub fn match_expr(
|
||||
|
||||
// If we get here, match everything
|
||||
// TODO: Borrow some cases?
|
||||
let prev_value = patterns.insert(CowStr::Borrowed(pat.name), CowStr::Owned(value.to_owned()));
|
||||
let prev_value = patterns.insert(pat.name.clone(), value);
|
||||
assert_matches!(prev_value, None, "Found repeated pattern");
|
||||
cur_cmpts = &[];
|
||||
value = "";
|
||||
value = ArcStr::default();
|
||||
},
|
||||
|
||||
// If we have patterns on both sides, reject
|
||||
@ -82,7 +82,7 @@ pub fn match_expr(
|
||||
unreachable!("Cannot match unexpanded alias: {alias:?}"),
|
||||
|
||||
// If we're empty, we match an empty string
|
||||
[] => match value {
|
||||
[] => match &*value {
|
||||
"" => return Ok(Some(patterns)),
|
||||
_ => return Ok(None),
|
||||
},
|
||||
|
||||
@ -5,11 +5,11 @@ use {
|
||||
crate::{
|
||||
error::{AppError, ResultMultiple},
|
||||
rules::{AliasOp, Command, CommandArg, DepItem, Exec, Expr, ExprCmpt, OutItem, Rule, Target},
|
||||
util::CowStr,
|
||||
util::ArcStr,
|
||||
},
|
||||
indexmap::IndexMap,
|
||||
smallvec::SmallVec,
|
||||
std::{collections::BTreeMap, marker::PhantomData, path::PathBuf, sync::Arc},
|
||||
std::{collections::BTreeMap, marker::PhantomData, mem, path::PathBuf, sync::Arc},
|
||||
};
|
||||
|
||||
/// Expander
|
||||
@ -38,7 +38,7 @@ impl Expander {
|
||||
|
||||
// If it's a pattern, we visit it
|
||||
// Note: We don't care about the operations on patterns, those are for matching
|
||||
ExprCmpt::Pattern(pat) => match visitor.visit_pat(pat.name) {
|
||||
ExprCmpt::Pattern(pat) => match visitor.visit_pat(&pat.name) {
|
||||
// If expanded, just replace it with a string
|
||||
FlowControl::ExpandTo(value) => expr.push_str(value),
|
||||
|
||||
@ -46,12 +46,12 @@ impl Expander {
|
||||
FlowControl::Keep => expr.push(cmpt),
|
||||
FlowControl::Error =>
|
||||
return Err(AppError::UnknownPattern {
|
||||
pattern_name: pat.name.to_owned(),
|
||||
pattern_name: pat.name.to_string(),
|
||||
}),
|
||||
},
|
||||
|
||||
// If it's an alias, we visit and then expand it
|
||||
ExprCmpt::Alias(alias) => match visitor.visit_alias(alias.name) {
|
||||
ExprCmpt::Alias(alias) => match visitor.visit_alias(&alias.name) {
|
||||
// If expanded, check if we need to apply any operations
|
||||
FlowControl::ExpandTo(alias_expr) => match alias.ops.is_empty() {
|
||||
// If not, just recursively expand it
|
||||
@ -66,11 +66,12 @@ impl Expander {
|
||||
let value = self.expand_expr_string(alias_expr, visitor)?;
|
||||
|
||||
// Then apply all
|
||||
let value = alias.ops.iter().try_fold(value, |value, &op| {
|
||||
let s = CowStr::into_owned(value);
|
||||
self.expand_alias_op(op, s)
|
||||
.map(CowStr::from)
|
||||
.map_err(AppError::alias_op(op))
|
||||
let value = alias.ops.iter().try_fold(value, |mut value, &op| {
|
||||
value
|
||||
.with_mut(|s| self.expand_alias_op(op, s))
|
||||
.map_err(AppError::alias_op(op))?;
|
||||
|
||||
Ok(value)
|
||||
})?;
|
||||
|
||||
expr.push_str(&value);
|
||||
@ -81,7 +82,7 @@ impl Expander {
|
||||
FlowControl::Keep => expr.push(cmpt),
|
||||
FlowControl::Error =>
|
||||
return Err(AppError::UnknownAlias {
|
||||
alias_name: alias.name.to_owned(),
|
||||
alias_name: alias.name.to_string(),
|
||||
}),
|
||||
},
|
||||
};
|
||||
@ -91,7 +92,7 @@ impl Expander {
|
||||
}
|
||||
|
||||
/// Expands an expression into a string
|
||||
pub fn expand_expr_string(&self, expr: &Expr, visitor: &Visitor) -> Result<CowStr, AppError> {
|
||||
pub fn expand_expr_string(&self, expr: &Expr, visitor: &Visitor) -> Result<ArcStr, AppError> {
|
||||
self.expand_expr(expr, visitor)?
|
||||
.try_into_string()
|
||||
.map_err(|expr| AppError::UnresolvedAliasOrPats {
|
||||
@ -101,11 +102,11 @@ impl Expander {
|
||||
}
|
||||
|
||||
/// Expands an alias operation on the value of that alias
|
||||
fn expand_alias_op(&self, op: AliasOp, value: String) -> Result<String, AppError> {
|
||||
let value = match op {
|
||||
fn expand_alias_op(&self, op: AliasOp, value: &mut String) -> Result<(), AppError> {
|
||||
match op {
|
||||
AliasOp::DirName => {
|
||||
// Get the path and try to pop the last segment
|
||||
let mut path = PathBuf::from(value);
|
||||
let mut path = PathBuf::from(mem::take(value));
|
||||
if !path.pop() {
|
||||
return Err(AppError::PathParent { path });
|
||||
}
|
||||
@ -113,21 +114,22 @@ impl Expander {
|
||||
// Then convert it back to a string
|
||||
// Note: This should technically never fail, since the path was originally
|
||||
// utf-8
|
||||
path.into_os_string()
|
||||
*value = path
|
||||
.into_os_string()
|
||||
.into_string()
|
||||
.expect("utf-8 path was no longer utf-8 after getting dir-name")
|
||||
.expect("utf-8 path was no longer utf-8 after getting dir-name");
|
||||
},
|
||||
};
|
||||
|
||||
Ok(value)
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Expands a rule of all it's aliases and patterns
|
||||
pub fn expand_rule(&self, rule: &Rule<Expr>, visitor: &Visitor) -> Result<Rule<CowStr>, AppError> {
|
||||
pub fn expand_rule(&self, rule: &Rule<Expr>, visitor: &Visitor) -> Result<Rule<ArcStr>, AppError> {
|
||||
let aliases = rule
|
||||
.aliases
|
||||
.iter()
|
||||
.map(|(&name, expr)| Ok((name, self.expand_expr_string(expr, visitor)?)))
|
||||
.map(|(name, expr)| Ok((name.clone(), self.expand_expr_string(expr, visitor)?)))
|
||||
.collect::<ResultMultiple<_>>()?;
|
||||
|
||||
let output = rule
|
||||
@ -184,7 +186,7 @@ impl Expander {
|
||||
};
|
||||
|
||||
Ok(Rule {
|
||||
name: rule.name,
|
||||
name: rule.name.clone(),
|
||||
aliases: Arc::new(aliases),
|
||||
output,
|
||||
deps,
|
||||
@ -193,7 +195,7 @@ impl Expander {
|
||||
}
|
||||
|
||||
/// Expands a command
|
||||
pub fn expand_cmd(&self, cmd: &Command<Expr>, visitor: &Visitor) -> Result<Command<CowStr>, AppError> {
|
||||
pub fn expand_cmd(&self, cmd: &Command<Expr>, visitor: &Visitor) -> Result<Command<ArcStr>, AppError> {
|
||||
Ok(Command {
|
||||
cwd: cmd
|
||||
.cwd
|
||||
@ -214,7 +216,7 @@ impl Expander {
|
||||
}
|
||||
|
||||
/// Expands a target expression
|
||||
pub fn expand_target(&self, target: &Target<Expr>, visitor: &Visitor) -> Result<Target<CowStr>, AppError> {
|
||||
pub fn expand_target(&self, target: &Target<Expr>, visitor: &Visitor) -> Result<Target<ArcStr>, AppError> {
|
||||
let target = match *target {
|
||||
Target::File { ref file, is_static } => Target::File {
|
||||
file: self
|
||||
@ -275,24 +277,24 @@ impl<T> FlowControl<T> {
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Visitor {
|
||||
/// All aliases, in order to check
|
||||
aliases: SmallVec<[Arc<IndexMap<&'static str, Expr>>; 2]>,
|
||||
aliases: SmallVec<[Arc<IndexMap<ArcStr, Expr>>; 2]>,
|
||||
|
||||
/// All patterns, in order to check
|
||||
pats: SmallVec<[Arc<BTreeMap<CowStr, CowStr>>; 1]>,
|
||||
pats: SmallVec<[Arc<BTreeMap<ArcStr, ArcStr>>; 1]>,
|
||||
|
||||
/// Default alias action
|
||||
default_alias: FlowControl<Expr>,
|
||||
|
||||
/// Default pattern action
|
||||
default_pat: FlowControl<CowStr>,
|
||||
default_pat: FlowControl<ArcStr>,
|
||||
}
|
||||
|
||||
impl Visitor {
|
||||
/// Creates a new visitor with aliases and patterns
|
||||
pub fn new<'a, A, P>(aliases: A, pats: P) -> Self
|
||||
where
|
||||
A: IntoIterator<Item = &'a Arc<IndexMap<&'static str, Expr>>>,
|
||||
P: IntoIterator<Item = &'a Arc<BTreeMap<CowStr, CowStr>>>,
|
||||
A: IntoIterator<Item = &'a Arc<IndexMap<ArcStr, Expr>>>,
|
||||
P: IntoIterator<Item = &'a Arc<BTreeMap<ArcStr, ArcStr>>>,
|
||||
{
|
||||
Self {
|
||||
aliases: aliases.into_iter().map(Arc::clone).collect(),
|
||||
@ -305,13 +307,13 @@ impl Visitor {
|
||||
/// Creates a visitor from aliases
|
||||
pub fn from_aliases<'a, A>(aliases: A) -> Self
|
||||
where
|
||||
A: IntoIterator<Item = &'a Arc<IndexMap<&'static str, Expr>>>,
|
||||
A: IntoIterator<Item = &'a Arc<IndexMap<ArcStr, Expr>>>,
|
||||
{
|
||||
Self::new(aliases, [])
|
||||
}
|
||||
|
||||
/// Sets the default pattern
|
||||
pub fn with_default_pat(self, default_pat: FlowControl<CowStr>) -> Self {
|
||||
pub fn with_default_pat(self, default_pat: FlowControl<ArcStr>) -> Self {
|
||||
Self { default_pat, ..self }
|
||||
}
|
||||
|
||||
@ -327,7 +329,7 @@ impl Visitor {
|
||||
}
|
||||
|
||||
/// Visits a pattern
|
||||
fn visit_pat(&self, pat_name: &str) -> FlowControl<&CowStr> {
|
||||
fn visit_pat(&self, pat_name: &str) -> FlowControl<&ArcStr> {
|
||||
for pats in &self.pats {
|
||||
if let Some(pat) = pats.get(pat_name) {
|
||||
return FlowControl::ExpandTo(pat);
|
||||
|
||||
18
src/main.rs
18
src/main.rs
@ -12,7 +12,11 @@
|
||||
strict_provenance,
|
||||
assert_matches,
|
||||
try_trait_v2,
|
||||
if_let_guard
|
||||
if_let_guard,
|
||||
pattern,
|
||||
unsigned_signed_diff,
|
||||
vec_into_raw_parts,
|
||||
ptr_metadata
|
||||
)]
|
||||
// Lints
|
||||
#![allow(
|
||||
@ -55,7 +59,7 @@ use {
|
||||
thread,
|
||||
time::{Duration, SystemTime},
|
||||
},
|
||||
util::CowStr,
|
||||
util::ArcStr,
|
||||
watcher::Watcher,
|
||||
};
|
||||
|
||||
@ -84,13 +88,13 @@ async fn main() -> ExitResult {
|
||||
|
||||
// Parse the ast
|
||||
let zbuild_file = fs::read_to_string(zbuild_path).map_err(AppError::read_file(&zbuild_path))?;
|
||||
let zbuild_file = zbuild_file.leak();
|
||||
let zbuild_file = ArcStr::from(zbuild_file);
|
||||
tracing::trace!(?zbuild_file, "Read zbuild.yaml");
|
||||
let ast = serde_yaml::from_str::<Ast<'_>>(zbuild_file).map_err(AppError::parse_yaml(&zbuild_path))?;
|
||||
let ast = serde_yaml::from_str::<Ast<'_>>(&zbuild_file).map_err(AppError::parse_yaml(&zbuild_path))?;
|
||||
tracing::trace!(?ast, "Parsed ast");
|
||||
|
||||
// Build the rules
|
||||
let rules = Rules::new(ast);
|
||||
let rules = Rules::new(&zbuild_file, ast);
|
||||
tracing::trace!(?rules, "Built rules");
|
||||
|
||||
// Get the max number of jobs we can execute at once
|
||||
@ -130,7 +134,7 @@ async fn main() -> ExitResult {
|
||||
// If there was a rule, use it without any patterns
|
||||
// TODO: If it requires patterns maybe error out here?
|
||||
|rule| rules::Target::Rule {
|
||||
rule: rules::Expr::string(rule.name.to_owned()),
|
||||
rule: rules::Expr::string(rule.name.clone()),
|
||||
pats: Arc::new(BTreeMap::new()),
|
||||
},
|
||||
)
|
||||
@ -288,7 +292,7 @@ impl BuildableTargetInner for rules::Expr {
|
||||
}
|
||||
}
|
||||
|
||||
impl BuildableTargetInner for CowStr {
|
||||
impl BuildableTargetInner for ArcStr {
|
||||
async fn build(
|
||||
target: &rules::Target<Self>,
|
||||
builder: &Builder,
|
||||
|
||||
25
src/rules.rs
25
src/rules.rs
@ -19,7 +19,11 @@ pub use {
|
||||
};
|
||||
|
||||
// Imports
|
||||
use {crate::Ast, indexmap::IndexMap, std::sync::Arc};
|
||||
use {
|
||||
crate::{util::ArcStr, Ast},
|
||||
indexmap::IndexMap,
|
||||
std::sync::Arc,
|
||||
};
|
||||
|
||||
/// Rules.
|
||||
///
|
||||
@ -31,29 +35,36 @@ pub struct Rules {
|
||||
///
|
||||
/// These are available for the whole program to
|
||||
/// use.
|
||||
pub aliases: Arc<IndexMap<&'static str, Expr>>,
|
||||
pub aliases: Arc<IndexMap<ArcStr, Expr>>,
|
||||
|
||||
/// Default targets to build
|
||||
pub default: Vec<Target<Expr>>,
|
||||
|
||||
/// Rules
|
||||
pub rules: IndexMap<&'static str, Rule<Expr>>,
|
||||
pub rules: IndexMap<ArcStr, Rule<Expr>>,
|
||||
}
|
||||
|
||||
impl Rules {
|
||||
/// Creates all rules from the ast
|
||||
#[must_use]
|
||||
pub fn new(ast: Ast<'static>) -> Self {
|
||||
pub fn new(zbuild_file: &ArcStr, ast: Ast<'_>) -> Self {
|
||||
let aliases = ast
|
||||
.aliases
|
||||
.into_iter()
|
||||
.map(|(alias, value)| (alias, Expr::new(value)))
|
||||
.map(|(alias, value)| (zbuild_file.slice_from_str(alias), Expr::new(zbuild_file, value)))
|
||||
.collect();
|
||||
let default = ast
|
||||
.default
|
||||
.into_iter()
|
||||
.map(|target| Target::new(zbuild_file, target))
|
||||
.collect();
|
||||
let default = ast.default.into_iter().map(Target::new).collect();
|
||||
let rules = ast
|
||||
.rules
|
||||
.into_iter()
|
||||
.map(|(name, rule)| (name, Rule::new(name, rule)))
|
||||
.map(|(name, rule)| {
|
||||
let name = zbuild_file.slice_from_str(name);
|
||||
(name.clone(), Rule::new(zbuild_file, name, rule))
|
||||
})
|
||||
.collect();
|
||||
|
||||
Self {
|
||||
|
||||
@ -1,13 +1,13 @@
|
||||
//! Pattern
|
||||
|
||||
// Imports
|
||||
use std::fmt;
|
||||
use {crate::util::ArcStr, std::fmt};
|
||||
|
||||
/// Alias
|
||||
#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Hash, Debug)]
|
||||
pub struct Alias {
|
||||
/// Alias name
|
||||
pub name: &'static str,
|
||||
pub name: ArcStr,
|
||||
|
||||
/// Operators
|
||||
pub ops: Vec<AliasOp>,
|
||||
|
||||
@ -6,7 +6,7 @@ use {
|
||||
alias::{Alias, AliasOp},
|
||||
pattern::{Pattern, PatternOp},
|
||||
},
|
||||
crate::{ast, util::CowStr},
|
||||
crate::{ast, util::ArcStr},
|
||||
std::fmt,
|
||||
};
|
||||
|
||||
@ -14,7 +14,7 @@ use {
|
||||
#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Hash, Debug)]
|
||||
pub enum ExprCmpt {
|
||||
/// String
|
||||
String(CowStr),
|
||||
String(ArcStr),
|
||||
|
||||
/// Pattern
|
||||
Pattern(Pattern),
|
||||
@ -25,7 +25,7 @@ pub enum ExprCmpt {
|
||||
|
||||
impl ExprCmpt {
|
||||
/// Converts this expression into a string, if it's a string.
|
||||
pub fn try_into_string(self) -> Result<CowStr, Self> {
|
||||
pub fn try_into_string(self) -> Result<ArcStr, Self> {
|
||||
#[expect(clippy::wildcard_enum_match_arm, reason = "We only care about a specific variant")]
|
||||
match self {
|
||||
Self::String(v) => Ok(v),
|
||||
@ -49,24 +49,20 @@ impl Expr {
|
||||
}
|
||||
|
||||
/// Pushes a string into this expression
|
||||
pub fn push_str(&mut self, s: &CowStr) {
|
||||
pub fn push_str(&mut self, s: &ArcStr) {
|
||||
match self.cmpts.last_mut() {
|
||||
Some(ExprCmpt::String(last)) => {
|
||||
last.to_mut().push_str(s);
|
||||
},
|
||||
Some(ExprCmpt::String(last)) => last.with_mut(|last| last.push_str(s)),
|
||||
_ => self.cmpts.push(ExprCmpt::String(s.clone())),
|
||||
}
|
||||
}
|
||||
|
||||
/// Pushes a component into this expression
|
||||
pub fn push(&mut self, cmpt: &ExprCmpt) {
|
||||
#[expect(clippy::wildcard_enum_match_arm, reason = "The wildcard matches all variants")]
|
||||
match cmpt {
|
||||
// If it's a string, try to use `push_str` for merging strings.
|
||||
ExprCmpt::String(s) if let Some(ExprCmpt::String(last)) = self.cmpts.last_mut() =>
|
||||
last.to_mut().push_str(s),
|
||||
ExprCmpt::String(s) => self.push_str(s),
|
||||
|
||||
cmpt => self.cmpts.push(cmpt.clone()),
|
||||
cmpt @ (ExprCmpt::Alias(_) | ExprCmpt::Pattern(_)) => self.cmpts.push(cmpt.clone()),
|
||||
}
|
||||
}
|
||||
|
||||
@ -84,7 +80,7 @@ impl Expr {
|
||||
}
|
||||
|
||||
/// Converts this expression into a string, if it's compromised of only string components
|
||||
pub fn try_into_string(self) -> Result<CowStr, Self> {
|
||||
pub fn try_into_string(self) -> Result<ArcStr, Self> {
|
||||
// If all components aren't strings, return Err
|
||||
if !self.cmpts.iter().all(|cmpt| matches!(cmpt, ExprCmpt::String(_))) {
|
||||
return Err(self);
|
||||
@ -101,7 +97,7 @@ impl Expr {
|
||||
for cmpt in cmpts {
|
||||
let cmpt = cmpt.try_into_string().expect("Component wasn't a string");
|
||||
if !cmpt.is_empty() {
|
||||
output.to_mut().push_str(&cmpt);
|
||||
output.with_mut(|output| output.push_str(&cmpt));
|
||||
}
|
||||
}
|
||||
|
||||
@ -109,15 +105,15 @@ impl Expr {
|
||||
}
|
||||
|
||||
/// Creates a new expression from it's ast
|
||||
pub fn new(expr: ast::Expr<'static>) -> Self {
|
||||
pub fn new(zbuild_file: &ArcStr, expr: ast::Expr<'_>) -> Self {
|
||||
let cmpts = expr
|
||||
.cmpts
|
||||
.into_iter()
|
||||
.map(|cmpt| match cmpt {
|
||||
ast::ExprCmpt::String(s) => ExprCmpt::String(CowStr::Borrowed(s)),
|
||||
ast::ExprCmpt::String(s) => ExprCmpt::String(zbuild_file.slice_from_str(s)),
|
||||
ast::ExprCmpt::Pattern(ast::Pattern { name, ops }) => ExprCmpt::Pattern(Pattern {
|
||||
name,
|
||||
ops: ops
|
||||
name: zbuild_file.slice_from_str(name),
|
||||
ops: ops
|
||||
.into_iter()
|
||||
.map(|op| match op {
|
||||
ast::PatternOp::NonEmpty => PatternOp::NonEmpty,
|
||||
@ -125,8 +121,8 @@ impl Expr {
|
||||
.collect(),
|
||||
}),
|
||||
ast::ExprCmpt::Alias(ast::Alias { name, ops }) => ExprCmpt::Alias(Alias {
|
||||
name,
|
||||
ops: ops
|
||||
name: zbuild_file.slice_from_str(name),
|
||||
ops: ops
|
||||
.into_iter()
|
||||
.map(|op| match op {
|
||||
ast::AliasOp::DirName => AliasOp::DirName,
|
||||
@ -140,7 +136,7 @@ impl Expr {
|
||||
}
|
||||
|
||||
/// Returns an expression that's just a string
|
||||
pub fn string(value: impl Into<CowStr>) -> Self {
|
||||
pub fn string(value: impl Into<ArcStr>) -> Self {
|
||||
Self {
|
||||
cmpts: vec![ExprCmpt::String(value.into())],
|
||||
}
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
// Imports
|
||||
use {
|
||||
super::Expr,
|
||||
crate::ast,
|
||||
crate::{ast, util::ArcStr},
|
||||
std::{collections::BTreeMap, fmt, sync::Arc},
|
||||
};
|
||||
|
||||
@ -23,14 +23,14 @@ pub enum OutItem<T> {
|
||||
|
||||
impl OutItem<Expr> {
|
||||
/// Creates a new item from it's `ast`.
|
||||
pub fn new(item: ast::OutItem<'static>) -> Self {
|
||||
pub fn new(zbuild_file: &ArcStr, item: ast::OutItem<'_>) -> Self {
|
||||
match item {
|
||||
ast::OutItem::File(file) => Self::File {
|
||||
file: Expr::new(file),
|
||||
file: Expr::new(zbuild_file, file),
|
||||
is_deps_file: false,
|
||||
},
|
||||
ast::OutItem::DepsFile { deps_file } => Self::File {
|
||||
file: Expr::new(deps_file),
|
||||
file: Expr::new(zbuild_file, deps_file),
|
||||
is_deps_file: true,
|
||||
},
|
||||
}
|
||||
@ -83,10 +83,10 @@ pub enum DepItem<T> {
|
||||
|
||||
impl DepItem<Expr> {
|
||||
/// Creates a new item from it's `ast`.
|
||||
pub fn new(item: ast::DepItem<'static>) -> Self {
|
||||
pub fn new(zbuild_file: &ArcStr, item: ast::DepItem<'_>) -> Self {
|
||||
match item {
|
||||
ast::DepItem::File(file) => Self::File {
|
||||
file: Expr::new(file),
|
||||
file: Expr::new(zbuild_file, file),
|
||||
is_optional: false,
|
||||
is_static: false,
|
||||
is_deps_file: false,
|
||||
@ -94,28 +94,28 @@ impl DepItem<Expr> {
|
||||
ast::DepItem::Rule { rule, pats } => {
|
||||
let pats = pats
|
||||
.into_iter()
|
||||
.map(|(pat, value)| (Expr::new(pat), Expr::new(value)))
|
||||
.map(|(pat, value)| (Expr::new(zbuild_file, pat), Expr::new(zbuild_file, value)))
|
||||
.collect();
|
||||
Self::Rule {
|
||||
name: Expr::new(rule),
|
||||
name: Expr::new(zbuild_file, rule),
|
||||
pats: Arc::new(pats),
|
||||
}
|
||||
},
|
||||
ast::DepItem::DepsFile { deps_file } => Self::File {
|
||||
file: Expr::new(deps_file),
|
||||
file: Expr::new(zbuild_file, deps_file),
|
||||
is_optional: false,
|
||||
is_static: false,
|
||||
is_deps_file: true,
|
||||
},
|
||||
ast::DepItem::Static { item: static_item } => match static_item {
|
||||
ast::StaticDepItem::File(file) => Self::File {
|
||||
file: Expr::new(file),
|
||||
file: Expr::new(zbuild_file, file),
|
||||
is_optional: false,
|
||||
is_static: true,
|
||||
is_deps_file: false,
|
||||
},
|
||||
ast::StaticDepItem::DepsFile { deps_file } => Self::File {
|
||||
file: Expr::new(deps_file),
|
||||
file: Expr::new(zbuild_file, deps_file),
|
||||
is_optional: false,
|
||||
is_static: true,
|
||||
is_deps_file: true,
|
||||
@ -123,13 +123,13 @@ impl DepItem<Expr> {
|
||||
},
|
||||
ast::DepItem::Opt { item: opt_item } => match opt_item {
|
||||
ast::OptDepItem::File(file) => Self::File {
|
||||
file: Expr::new(file),
|
||||
file: Expr::new(zbuild_file, file),
|
||||
is_optional: true,
|
||||
is_static: true,
|
||||
is_deps_file: false,
|
||||
},
|
||||
ast::OptDepItem::DepsFile { deps_file } => Self::File {
|
||||
file: Expr::new(deps_file),
|
||||
file: Expr::new(zbuild_file, deps_file),
|
||||
is_optional: true,
|
||||
is_static: true,
|
||||
is_deps_file: true,
|
||||
|
||||
@ -1,13 +1,13 @@
|
||||
//! Pattern
|
||||
|
||||
// Imports
|
||||
use std::fmt;
|
||||
use {crate::util::ArcStr, std::fmt};
|
||||
|
||||
/// Pattern
|
||||
#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Hash, Debug)]
|
||||
pub struct Pattern {
|
||||
/// Pattern name
|
||||
pub name: &'static str,
|
||||
pub name: ArcStr,
|
||||
|
||||
/// Operators
|
||||
pub ops: Vec<PatternOp>,
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
// Imports
|
||||
use {
|
||||
super::{DepItem, Expr, OutItem},
|
||||
crate::ast,
|
||||
crate::{ast, util::ArcStr},
|
||||
indexmap::IndexMap,
|
||||
std::sync::Arc,
|
||||
};
|
||||
@ -12,10 +12,10 @@ use {
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Rule<T> {
|
||||
/// Name
|
||||
pub name: &'static str,
|
||||
pub name: ArcStr,
|
||||
|
||||
/// Aliases
|
||||
pub aliases: Arc<IndexMap<&'static str, T>>,
|
||||
pub aliases: Arc<IndexMap<ArcStr, T>>,
|
||||
|
||||
/// Output items
|
||||
pub output: Vec<OutItem<T>>,
|
||||
@ -29,15 +29,19 @@ pub struct Rule<T> {
|
||||
|
||||
impl Rule<Expr> {
|
||||
/// Creates a new rule from it's ast
|
||||
pub fn new(name: &'static str, rule: ast::Rule<'static>) -> Self {
|
||||
pub fn new(zbuild_file: &ArcStr, name: ArcStr, rule: ast::Rule<'_>) -> Self {
|
||||
let aliases = rule
|
||||
.aliases
|
||||
.into_iter()
|
||||
.map(|(alias, expr)| (alias, Expr::new(expr)))
|
||||
.map(|(alias, expr)| (zbuild_file.slice_from_str(alias), Expr::new(zbuild_file, expr)))
|
||||
.collect();
|
||||
let output = rule.out.into_iter().map(OutItem::new).collect();
|
||||
let deps = rule.deps.into_iter().map(DepItem::new).collect();
|
||||
let exec = Exec::new(rule.exec);
|
||||
let output = rule.out.into_iter().map(|out| OutItem::new(zbuild_file, out)).collect();
|
||||
let deps = rule
|
||||
.deps
|
||||
.into_iter()
|
||||
.map(|dep| DepItem::new(zbuild_file, dep))
|
||||
.collect();
|
||||
let exec = Exec::new(zbuild_file, rule.exec);
|
||||
|
||||
Self {
|
||||
name,
|
||||
@ -59,9 +63,13 @@ pub struct Exec<T> {
|
||||
|
||||
impl Exec<Expr> {
|
||||
/// Creates a new exec from it's ast
|
||||
pub fn new(exec: ast::Exec<'static>) -> Self {
|
||||
pub fn new(zbuild_file: &ArcStr, exec: ast::Exec<'_>) -> Self {
|
||||
Self {
|
||||
cmds: exec.cmds.into_iter().map(Command::new).collect(),
|
||||
cmds: exec
|
||||
.cmds
|
||||
.into_iter()
|
||||
.map(|cmd| Command::new(zbuild_file, cmd))
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -79,15 +87,15 @@ pub struct Command<T> {
|
||||
|
||||
impl Command<Expr> {
|
||||
/// Creates a new command from it's ast
|
||||
pub fn new(cmd: ast::Command<'static>) -> Self {
|
||||
pub fn new(zbuild_file: &ArcStr, cmd: ast::Command<'_>) -> Self {
|
||||
match cmd {
|
||||
ast::Command::OnlyArgs(args) => Self {
|
||||
cwd: None,
|
||||
args: args.into_iter().map(CommandArg::new).collect(),
|
||||
args: args.into_iter().map(|arg| CommandArg::new(zbuild_file, arg)).collect(),
|
||||
},
|
||||
ast::Command::Full { cwd, args } => Self {
|
||||
cwd: cwd.map(Expr::new),
|
||||
args: args.into_iter().map(CommandArg::new).collect(),
|
||||
cwd: cwd.map(|cwd| Expr::new(zbuild_file, cwd)),
|
||||
args: args.into_iter().map(|arg| CommandArg::new(zbuild_file, arg)).collect(),
|
||||
},
|
||||
}
|
||||
}
|
||||
@ -102,9 +110,9 @@ pub enum CommandArg<T> {
|
||||
|
||||
impl CommandArg<Expr> {
|
||||
/// Creates a new command argument from it's ast
|
||||
pub fn new(arg: ast::CommandArg<'static>) -> Self {
|
||||
pub fn new(zbuild_file: &ArcStr, arg: ast::CommandArg<'_>) -> Self {
|
||||
match arg {
|
||||
ast::CommandArg::Expr(expr) => Self::Expr(Expr::new(expr)),
|
||||
ast::CommandArg::Expr(expr) => Self::Expr(Expr::new(zbuild_file, expr)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
// Imports
|
||||
use {
|
||||
super::Expr,
|
||||
crate::{ast, util::CowStr},
|
||||
crate::{ast, util::ArcStr},
|
||||
std::{
|
||||
collections::BTreeMap,
|
||||
fmt,
|
||||
@ -31,7 +31,7 @@ pub enum Target<T> {
|
||||
rule: T,
|
||||
|
||||
/// Patterns
|
||||
pats: Arc<BTreeMap<CowStr, T>>,
|
||||
pats: Arc<BTreeMap<ArcStr, T>>,
|
||||
},
|
||||
}
|
||||
|
||||
@ -47,14 +47,14 @@ impl<T> Target<T> {
|
||||
|
||||
impl Target<Expr> {
|
||||
/// Creates a new target from it's ast
|
||||
pub fn new(ast: ast::Target<'static>) -> Self {
|
||||
pub fn new(zbuild_file: &ArcStr, ast: ast::Target<'_>) -> Self {
|
||||
match ast {
|
||||
ast::Target::File(file) => Self::File {
|
||||
file: Expr::new(file),
|
||||
file: Expr::new(zbuild_file, file),
|
||||
is_static: false,
|
||||
},
|
||||
ast::Target::Rule { rule } => Self::Rule {
|
||||
rule: Expr::new(rule),
|
||||
rule: Expr::new(zbuild_file, rule),
|
||||
pats: Arc::new(BTreeMap::new()),
|
||||
},
|
||||
}
|
||||
|
||||
12
src/util.rs
12
src/util.rs
@ -1,11 +1,16 @@
|
||||
//! Utilities
|
||||
|
||||
// Modules
|
||||
pub mod arc_str;
|
||||
|
||||
// Exports
|
||||
pub use self::arc_str::ArcStr;
|
||||
|
||||
// Imports
|
||||
use {
|
||||
futures::Future,
|
||||
pin_project::pin_project,
|
||||
std::{
|
||||
borrow::Cow,
|
||||
io,
|
||||
path::{self, Path, PathBuf},
|
||||
pin::Pin,
|
||||
@ -15,11 +20,6 @@ use {
|
||||
tokio::fs,
|
||||
};
|
||||
|
||||
/// Alias for `Cow<'static, str>`
|
||||
// TODO: Replace this with some type like `(Arc<str>, Range<usize>)`
|
||||
// to allow for cheap `clone`ing, which we perform a lot?
|
||||
pub type CowStr = Cow<'static, str>;
|
||||
|
||||
/// Chains together any number of `IntoIterator`s
|
||||
pub macro chain {
|
||||
($lhs:expr, $rhs:expr $(,)?) => {
|
||||
|
||||
224
src/util/arc_str.rs
Normal file
224
src/util/arc_str.rs
Normal file
@ -0,0 +1,224 @@
|
||||
//! Arc string
|
||||
|
||||
// Lints
|
||||
#![expect(unsafe_code, reason = "We need unsafe to implement our string 'cached' pointer")]
|
||||
|
||||
// Imports
|
||||
use std::{
|
||||
borrow::Borrow,
|
||||
cmp,
|
||||
fmt,
|
||||
hash::{Hash, Hasher},
|
||||
ops::Deref,
|
||||
ptr::NonNull,
|
||||
str::pattern::{Pattern, ReverseSearcher},
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
/// Arc string.
|
||||
///
|
||||
/// Stores a string as a (theoretical) `(Arc<str>, Range<usize>)`,
|
||||
/// to allow for fast indexing and cloning.
|
||||
///
|
||||
/// The actual implementation stores a `(&str, Arc<String>)` for fast
|
||||
/// access to the string, while retaining the ability to be cheaply
|
||||
/// accessible as a `String`.
|
||||
#[derive(Clone)]
|
||||
pub struct ArcStr {
|
||||
/// This string's pointer
|
||||
///
|
||||
/// The string must *never* be mutated through this pointer,
|
||||
/// due to it being possibly derived from a `&str`.
|
||||
ptr: NonNull<str>,
|
||||
|
||||
/// Inner
|
||||
#[expect(clippy::rc_buffer, reason = "We need it for efficient conversion to/from `String`")]
|
||||
inner: Arc<String>,
|
||||
}
|
||||
|
||||
impl ArcStr {
|
||||
/// Returns the offset of this string compared to the base
|
||||
fn base_offset(&self) -> usize {
|
||||
// SAFETY: `self.ptr` was derived from `inner.base_ptr`
|
||||
let start = unsafe { self.ptr.as_ptr().byte_offset_from(self.inner.as_ptr()) };
|
||||
usize::try_from(start).expect("String pointer was before base pointer")
|
||||
}
|
||||
|
||||
/// Updates this string as a `&mut String`.
|
||||
///
|
||||
/// Copies the string unless no other copies exist
|
||||
pub fn with_mut<F, R>(&mut self, f: F) -> R
|
||||
where
|
||||
F: FnOnce(&mut String) -> R,
|
||||
{
|
||||
// Get the offset and length of our specific string
|
||||
let start = self.base_offset();
|
||||
let len = self.len();
|
||||
|
||||
// Get the inner string
|
||||
let s = match Arc::get_mut(&mut self.inner) {
|
||||
// If we're unique, slice the parts we don't care about and return
|
||||
Some(s) => {
|
||||
s.truncate(start + len);
|
||||
let _ = s.drain(..start);
|
||||
|
||||
s
|
||||
},
|
||||
|
||||
// Otherwise copy
|
||||
None => {
|
||||
self.inner = Arc::new(self.to_string());
|
||||
Arc::get_mut(&mut self.inner).expect("Should be unique")
|
||||
},
|
||||
};
|
||||
|
||||
// Since we're invalidating `self.inner`, replace `ptr`
|
||||
// with a dummy value in case of panics.
|
||||
self.ptr = NonNull::from("");
|
||||
|
||||
// Then mutate
|
||||
let output = f(s);
|
||||
|
||||
// And finally, reconstruct ourselves
|
||||
self.ptr = NonNull::from(s.as_str());
|
||||
|
||||
output
|
||||
}
|
||||
|
||||
/// Creates a sub-slice of `self` containing `s`.
|
||||
///
|
||||
/// # Panics
|
||||
/// `s` must be derived from this string, else this method panics.
|
||||
pub fn slice_from_str(&self, s: &str) -> Self {
|
||||
// Get pointer ranges
|
||||
let self_range = self.as_bytes().as_ptr_range();
|
||||
let s_range = s.as_bytes().as_ptr_range();
|
||||
|
||||
assert!(
|
||||
self_range.contains(&s_range.start) || s_range.start == self_range.end,
|
||||
"String start was before this string"
|
||||
);
|
||||
assert!(
|
||||
self_range.contains(&s_range.end) || s_range.end == self_range.end,
|
||||
"String end was past this string"
|
||||
);
|
||||
|
||||
Self {
|
||||
ptr: NonNull::from(s),
|
||||
inner: Arc::clone(&self.inner),
|
||||
}
|
||||
}
|
||||
|
||||
/// Wrapper for [`str::strip_prefix`]
|
||||
pub fn strip_prefix<P: Pattern>(&self, prefix: P) -> Option<Self> {
|
||||
(**self).strip_prefix(prefix).map(|s| self.slice_from_str(s))
|
||||
}
|
||||
|
||||
/// Wrapper for [`str::strip_suffix`]
|
||||
pub fn strip_suffix<P: Pattern>(&self, suffix: P) -> Option<Self>
|
||||
where
|
||||
for<'a> P::Searcher<'a>: ReverseSearcher<'a>,
|
||||
{
|
||||
(**self).strip_suffix(suffix).map(|s| self.slice_from_str(s))
|
||||
}
|
||||
}
|
||||
|
||||
// SAFETY: We're a self-referential `(&str, Arc<String>)`,
|
||||
// which is comprised of `Send + Sync` types.
|
||||
unsafe impl Send for ArcStr {}
|
||||
|
||||
// SAFETY: See above in [`Send`] impl
|
||||
unsafe impl Sync for ArcStr {}
|
||||
|
||||
|
||||
impl PartialEq for ArcStr {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.cmp(other).is_eq()
|
||||
}
|
||||
}
|
||||
impl Eq for ArcStr {}
|
||||
|
||||
impl PartialOrd for ArcStr {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
impl Ord for ArcStr {
|
||||
fn cmp(&self, other: &Self) -> cmp::Ordering {
|
||||
(**self).cmp(&**other)
|
||||
}
|
||||
}
|
||||
|
||||
impl Hash for ArcStr {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
(**self).hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for ArcStr {
|
||||
fn default() -> Self {
|
||||
String::new().into()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for ArcStr {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
(**self).fmt(f)
|
||||
}
|
||||
}
|
||||
impl fmt::Debug for ArcStr {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
(**self).fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for ArcStr {
|
||||
type Target = str;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
// SAFETY: `self.ptr` always contains a valid `str`.
|
||||
unsafe { self.ptr.as_ref() }
|
||||
}
|
||||
}
|
||||
|
||||
impl Borrow<str> for ArcStr {
|
||||
fn borrow(&self) -> &str {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl From<String> for ArcStr {
|
||||
fn from(s: String) -> Self {
|
||||
Self {
|
||||
ptr: NonNull::from(s.as_str()),
|
||||
inner: Arc::new(s),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ArcStr> for String {
|
||||
fn from(s: ArcStr) -> Self {
|
||||
// Get the offset and length of our specific string
|
||||
let start = s.base_offset();
|
||||
let len = s.len();
|
||||
|
||||
match Arc::try_unwrap(s.inner) {
|
||||
// If we're unique, slice the parts we don't care about and return
|
||||
Ok(mut inner) => {
|
||||
inner.truncate(start + len);
|
||||
let _ = inner.drain(..start);
|
||||
|
||||
inner
|
||||
},
|
||||
|
||||
// Otherwise copy
|
||||
Err(inner) => ArcStr { inner, ..s }.to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&str> for ArcStr {
|
||||
fn from(s: &str) -> Self {
|
||||
s.to_owned().into()
|
||||
}
|
||||
}
|
||||
@ -8,7 +8,7 @@
|
||||
|
||||
// Imports
|
||||
use {
|
||||
crate::{build, rules::Target, util::CowStr, AppError, Builder, Rules},
|
||||
crate::{build, rules::Target, util::ArcStr, AppError, Builder, Rules},
|
||||
anyhow::Context,
|
||||
dashmap::{DashMap, DashSet},
|
||||
futures::{stream::FuturesUnordered, StreamExt},
|
||||
@ -28,10 +28,10 @@ use {
|
||||
#[derive(Clone, Debug)]
|
||||
struct RevDep {
|
||||
/// Target of the dependency
|
||||
target: Target<CowStr>,
|
||||
target: Target<ArcStr>,
|
||||
|
||||
/// All parent targets
|
||||
parents: Arc<DashSet<Target<CowStr>>>,
|
||||
parents: Arc<DashSet<Target<ArcStr>>>,
|
||||
}
|
||||
|
||||
/// Target watcher
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user