Compare commits
3 Commits
3b6804dde6
...
b4e0b53cde
| Author | SHA1 | Date | |
|---|---|---|---|
|
b4e0b53cde
|
|||
|
6cd87aa653
|
|||
|
a8683e720b
|
@@ -46,44 +46,50 @@ export const resolvePath = (path: NixValue): string => {
|
|||||||
return Deno.core.ops.op_resolve_path(path_str);
|
return Deno.core.ops.op_resolve_path(path_str);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
export const select = (obj: NixValue, attrpath: NixValue[]): NixValue => {
|
||||||
* Select an attribute from an attribute set
|
let attrs = forceAttrs(obj);
|
||||||
* Used by codegen for attribute access (e.g., obj.key)
|
|
||||||
*
|
|
||||||
* @param obj - Attribute set to select from
|
|
||||||
* @param key - Key to select
|
|
||||||
* @returns The value at obj[key]
|
|
||||||
* @throws Error if obj is null/undefined or key not found
|
|
||||||
*/
|
|
||||||
export const select = (obj: NixValue, key: NixValue): NixValue => {
|
|
||||||
const forced_obj = forceAttrs(obj);
|
|
||||||
const forced_key = forceString(key);
|
|
||||||
|
|
||||||
if (!(forced_key in forced_obj)) {
|
for (const attr of attrpath.slice(0, -1)) {
|
||||||
throw new Error(`Attribute '${forced_key}' not found`);
|
const key = forceString(attr)
|
||||||
|
if (!(key in attrs)) {
|
||||||
|
throw new Error(`Attribute '${key}' not found`);
|
||||||
|
}
|
||||||
|
const cur = force(attrs[forceString(attr)]);
|
||||||
|
if (!isAttrs(cur)) {
|
||||||
|
// throw new Error(`Attribute '${forced_key}' not found`);
|
||||||
|
// FIXME: error
|
||||||
|
throw new Error(`Attribute not found`);
|
||||||
|
}
|
||||||
|
attrs = cur;
|
||||||
}
|
}
|
||||||
|
|
||||||
return forced_obj[forced_key];
|
const last = forceString(attrpath[attrpath.length - 1])
|
||||||
|
if (!(last in attrs)) {
|
||||||
|
throw new Error(`Attribute '${last}' not found`);
|
||||||
|
}
|
||||||
|
return attrs[last];
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
export const selectWithDefault = (obj: NixValue, attrpath: NixValue[], default_val: NixValue): NixValue => {
|
||||||
* Select an attribute with a default value
|
let attrs = forceAttrs(obj);
|
||||||
* Used for Nix's `obj.key or default` syntax
|
|
||||||
*
|
|
||||||
* @param obj - Attribute set to select from
|
|
||||||
* @param key - Key to select
|
|
||||||
* @param default_val - Value to return if key not found (will be forced if it's a thunk)
|
|
||||||
* @returns obj[key] if exists, otherwise force(default_val)
|
|
||||||
*/
|
|
||||||
export const selectWithDefault = (obj: NixValue, key: NixValue, default_val: NixValue): NixValue => {
|
|
||||||
const attrs = forceAttrs(obj);
|
|
||||||
const forced_key = forceString(key);
|
|
||||||
|
|
||||||
if (!(forced_key in attrs)) {
|
for (const attr of attrpath.slice(0, -1)) {
|
||||||
return force(default_val);
|
const key = forceString(attr)
|
||||||
|
if (!(key in attrs)) {
|
||||||
|
return default_val
|
||||||
|
}
|
||||||
|
const cur = force(attrs[key]);
|
||||||
|
if (!isAttrs(cur)) {
|
||||||
|
return default_val;
|
||||||
|
}
|
||||||
|
attrs = cur;
|
||||||
}
|
}
|
||||||
|
|
||||||
return attrs[forced_key];
|
const last = forceString(attrpath[attrpath.length - 1]);
|
||||||
|
if (last in attrs) {
|
||||||
|
return attrs[last]
|
||||||
|
}
|
||||||
|
return default_val;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const hasAttr = (obj: NixValue, attrpath: NixValue[]): NixBool => {
|
export const hasAttr = (obj: NixValue, attrpath: NixValue[]): NixBool => {
|
||||||
@@ -93,14 +99,14 @@ export const hasAttr = (obj: NixValue, attrpath: NixValue[]): NixBool => {
|
|||||||
let attrs = obj;
|
let attrs = obj;
|
||||||
|
|
||||||
for (const attr of attrpath.slice(0, -1)) {
|
for (const attr of attrpath.slice(0, -1)) {
|
||||||
const cur = attrs[forceString(attr)];
|
const cur = force(attrs[forceString(attr)]);
|
||||||
if (!isAttrs(cur)) {
|
if (!isAttrs(cur)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
attrs = cur;
|
attrs = cur;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return forceString(attrpath[attrpath.length - 1]) in attrs;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -11,10 +11,15 @@ pub(crate) trait CodegenContext {
|
|||||||
fn get_sym(&self, id: SymId) -> &str;
|
fn get_sym(&self, id: SymId) -> &str;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn escape_quote_string(s: &str) -> String {
|
trait EscapeQuote {
|
||||||
let mut escaped = String::with_capacity(s.len() + 2);
|
fn escape_quote(&self) -> String;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EscapeQuote for str {
|
||||||
|
fn escape_quote(&self) -> String {
|
||||||
|
let mut escaped = String::with_capacity(self.len() + 2);
|
||||||
escaped.push('"');
|
escaped.push('"');
|
||||||
for c in s.chars() {
|
for c in self.chars() {
|
||||||
match c {
|
match c {
|
||||||
'\\' => escaped.push_str("\\\\"),
|
'\\' => escaped.push_str("\\\\"),
|
||||||
'\"' => escaped.push_str("\\\""),
|
'\"' => escaped.push_str("\\\""),
|
||||||
@@ -26,6 +31,7 @@ fn escape_quote_string(s: &str) -> String {
|
|||||||
}
|
}
|
||||||
escaped.push('"');
|
escaped.push('"');
|
||||||
escaped
|
escaped
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Ctx: CodegenContext> Compile<Ctx> for Ir {
|
impl<Ctx: CodegenContext> Compile<Ctx> for Ir {
|
||||||
@@ -33,7 +39,7 @@ impl<Ctx: CodegenContext> Compile<Ctx> for Ir {
|
|||||||
match self {
|
match self {
|
||||||
Ir::Int(int) => format!("{int}n"), // Generate BigInt literal
|
Ir::Int(int) => format!("{int}n"), // Generate BigInt literal
|
||||||
Ir::Float(float) => float.to_string(),
|
Ir::Float(float) => float.to_string(),
|
||||||
Ir::Str(s) => escape_quote_string(&s.val),
|
Ir::Str(s) => s.val.escape_quote(),
|
||||||
Ir::Path(p) => {
|
Ir::Path(p) => {
|
||||||
// Path needs runtime resolution for interpolated paths
|
// Path needs runtime resolution for interpolated paths
|
||||||
let path_expr = ctx.get_ir(p.expr).compile(ctx);
|
let path_expr = ctx.get_ir(p.expr).compile(ctx);
|
||||||
@@ -62,7 +68,9 @@ impl<Ctx: CodegenContext> Compile<Ctx> for Ir {
|
|||||||
format!("expr{}", expr_id.0)
|
format!("expr{}", expr_id.0)
|
||||||
}
|
}
|
||||||
Ir::Builtins(_) => "Nix.builtins".to_string(),
|
Ir::Builtins(_) => "Nix.builtins".to_string(),
|
||||||
&Ir::Builtin(Builtin(name)) => format!("Nix.builtins[\"{}\"]", ctx.get_sym(name)),
|
&Ir::Builtin(Builtin(name)) => {
|
||||||
|
format!("Nix.builtins[{}]", ctx.get_sym(name).escape_quote())
|
||||||
|
}
|
||||||
Ir::ConcatStrings(x) => x.compile(ctx),
|
Ir::ConcatStrings(x) => x.compile(ctx),
|
||||||
Ir::HasAttr(x) => x.compile(ctx),
|
Ir::HasAttr(x) => x.compile(ctx),
|
||||||
&Ir::Assert(Assert { assertion, expr }) => {
|
&Ir::Assert(Assert { assertion, expr }) => {
|
||||||
@@ -145,7 +153,7 @@ impl Func {
|
|||||||
let required = if let Some(req) = &self.param.required {
|
let required = if let Some(req) = &self.param.required {
|
||||||
let keys: Vec<_> = req
|
let keys: Vec<_> = req
|
||||||
.iter()
|
.iter()
|
||||||
.map(|&sym| format!("\"{}\"", ctx.get_sym(sym)))
|
.map(|&sym| ctx.get_sym(sym).escape_quote())
|
||||||
.collect();
|
.collect();
|
||||||
format!("[{}]", keys.join(","))
|
format!("[{}]", keys.join(","))
|
||||||
} else {
|
} else {
|
||||||
@@ -156,7 +164,7 @@ impl Func {
|
|||||||
let allowed = if let Some(allow) = &self.param.allowed {
|
let allowed = if let Some(allow) = &self.param.allowed {
|
||||||
let keys: Vec<_> = allow
|
let keys: Vec<_> = allow
|
||||||
.iter()
|
.iter()
|
||||||
.map(|&sym| format!("\"{}\"", ctx.get_sym(sym)))
|
.map(|&sym| ctx.get_sym(sym).escape_quote())
|
||||||
.collect();
|
.collect();
|
||||||
format!("[{}]", keys.join(","))
|
format!("[{}]", keys.join(","))
|
||||||
} else {
|
} else {
|
||||||
@@ -217,47 +225,21 @@ impl<Ctx: CodegenContext> Compile<Ctx> for Let {
|
|||||||
|
|
||||||
impl<Ctx: CodegenContext> Compile<Ctx> for Select {
|
impl<Ctx: CodegenContext> Compile<Ctx> for Select {
|
||||||
fn compile(&self, ctx: &Ctx) -> String {
|
fn compile(&self, ctx: &Ctx) -> String {
|
||||||
let expr = ctx.get_ir(self.expr).compile(ctx);
|
let lhs = ctx.get_ir(self.expr).compile(ctx);
|
||||||
|
let attrpath = self
|
||||||
let mut result = expr;
|
.attrpath
|
||||||
let attr_count = self.attrpath.len();
|
.iter()
|
||||||
|
.map(|attr| match attr {
|
||||||
for (i, attr) in self.attrpath.iter().enumerate() {
|
Attr::Str(sym) => ctx.get_sym(*sym).escape_quote(),
|
||||||
let is_last = i == attr_count - 1;
|
Attr::Dynamic(expr_id) => ctx.get_ir(*expr_id).compile(ctx),
|
||||||
result = match attr {
|
})
|
||||||
Attr::Str(sym) => {
|
.join(",");
|
||||||
let key = ctx.get_sym(*sym);
|
if let Some(default) = self.default {
|
||||||
if let Some(default) = self.default
|
format!("Nix.selectWithDefault({lhs}, [{attrpath}], {})", ctx.get_ir(default).compile(ctx))
|
||||||
&& is_last
|
|
||||||
{
|
|
||||||
let default_val = ctx.get_ir(default).compile(ctx);
|
|
||||||
format!(
|
|
||||||
"Nix.selectWithDefault({}, \"{}\", {})",
|
|
||||||
result, key, default_val
|
|
||||||
)
|
|
||||||
} else {
|
} else {
|
||||||
format!("Nix.select({}, \"{}\")", result, key)
|
format!("Nix.select({lhs}, [{attrpath}])")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Attr::Dynamic(expr_id) => {
|
|
||||||
let key = ctx.get_ir(*expr_id).compile(ctx);
|
|
||||||
if let Some(default) = self.default
|
|
||||||
&& is_last
|
|
||||||
{
|
|
||||||
let default_val = ctx.get_ir(default).compile(ctx);
|
|
||||||
format!(
|
|
||||||
"Nix.selectWithDefault({}, {}, {})",
|
|
||||||
result, key, default_val
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
format!("Nix.select({}, {})", result, key)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
result
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Ctx: CodegenContext> Compile<Ctx> for AttrSet {
|
impl<Ctx: CodegenContext> Compile<Ctx> for AttrSet {
|
||||||
@@ -267,7 +249,7 @@ impl<Ctx: CodegenContext> Compile<Ctx> for AttrSet {
|
|||||||
for (&sym, &expr) in &self.stcs {
|
for (&sym, &expr) in &self.stcs {
|
||||||
let key = ctx.get_sym(sym);
|
let key = ctx.get_sym(sym);
|
||||||
let value = ctx.get_ir(expr).compile(ctx);
|
let value = ctx.get_ir(expr).compile(ctx);
|
||||||
attrs.push(format!("{}: {}", escape_quote_string(key), value));
|
attrs.push(format!("{}: {}", key.escape_quote(), value));
|
||||||
}
|
}
|
||||||
|
|
||||||
for (key_expr, value_expr) in &self.dyns {
|
for (key_expr, value_expr) in &self.dyns {
|
||||||
@@ -310,9 +292,7 @@ impl<Ctx: CodegenContext> Compile<Ctx> for HasAttr {
|
|||||||
.rhs
|
.rhs
|
||||||
.iter()
|
.iter()
|
||||||
.map(|attr| match attr {
|
.map(|attr| match attr {
|
||||||
Attr::Str(sym) => {
|
Attr::Str(sym) => ctx.get_sym(*sym).escape_quote(),
|
||||||
format!("\"{}\"", ctx.get_sym(*sym))
|
|
||||||
}
|
|
||||||
Attr::Dynamic(expr_id) => ctx.get_ir(*expr_id).compile(ctx),
|
Attr::Dynamic(expr_id) => ctx.get_ir(*expr_id).compile(ctx),
|
||||||
})
|
})
|
||||||
.join(",");
|
.join(",");
|
||||||
|
|||||||
@@ -105,8 +105,11 @@ impl DowngradeContext for DowngradeCtx<'_> {
|
|||||||
.rposition(|t| t.current_binding.is_some());
|
.rposition(|t| t.current_binding.is_some());
|
||||||
|
|
||||||
// Record dependency if both exist
|
// Record dependency if both exist
|
||||||
if let (Some(expr_idx), Some(curr_idx)) = (expr_tracker_idx, current_tracker_idx) {
|
if let (Some(expr_idx), Some(curr_idx)) =
|
||||||
let current_binding = self.dep_tracker_stack[curr_idx].current_binding.unwrap();
|
(expr_tracker_idx, current_tracker_idx)
|
||||||
|
{
|
||||||
|
let current_binding =
|
||||||
|
self.dep_tracker_stack[curr_idx].current_binding.unwrap();
|
||||||
let owner_binding = self.dep_tracker_stack[curr_idx].owner_binding;
|
let owner_binding = self.dep_tracker_stack[curr_idx].owner_binding;
|
||||||
|
|
||||||
// If referencing from inner scope to outer scope
|
// If referencing from inner scope to outer scope
|
||||||
@@ -120,17 +123,17 @@ impl DowngradeContext for DowngradeCtx<'_> {
|
|||||||
tracker.graph.add_edge(from_node, to_node, ());
|
tracker.graph.add_edge(from_node, to_node, ());
|
||||||
} else if curr_idx > expr_idx {
|
} else if curr_idx > expr_idx {
|
||||||
// Cross-scope reference: use owner_binding if available
|
// Cross-scope reference: use owner_binding if available
|
||||||
if let Some(owner) = owner_binding {
|
if let Some(owner) = owner_binding
|
||||||
if let (Some(&from_node), Some(&to_node)) = (
|
&& let (Some(&from_node), Some(&to_node)) = (
|
||||||
tracker.expr_to_node.get(&owner),
|
tracker.expr_to_node.get(&owner),
|
||||||
tracker.expr_to_node.get(&expr),
|
tracker.expr_to_node.get(&expr),
|
||||||
) {
|
)
|
||||||
|
{
|
||||||
tracker.graph.add_edge(from_node, to_node, ());
|
tracker.graph.add_edge(from_node, to_node, ());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return Ok(self.new_expr(Ir::ExprRef(expr)));
|
return Ok(self.new_expr(Ir::ExprRef(expr)));
|
||||||
}
|
}
|
||||||
@@ -273,7 +276,9 @@ impl DowngradeContext for DowngradeCtx<'_> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn get_current_binding(&self) -> Option<ExprId> {
|
fn get_current_binding(&self) -> Option<ExprId> {
|
||||||
self.dep_tracker_stack.last().and_then(|t| t.current_binding)
|
self.dep_tracker_stack
|
||||||
|
.last()
|
||||||
|
.and_then(|t| t.current_binding)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_current_binding(&mut self, expr: Option<ExprId>) {
|
fn set_current_binding(&mut self, expr: Option<ExprId>) {
|
||||||
|
|||||||
@@ -121,10 +121,7 @@ impl Error {
|
|||||||
Self::new(ErrorKind::DowngradeError(msg))
|
Self::new(ErrorKind::DowngradeError(msg))
|
||||||
}
|
}
|
||||||
pub fn eval_error(msg: String, backtrace: Option<String>) -> Self {
|
pub fn eval_error(msg: String, backtrace: Option<String>) -> Self {
|
||||||
Self::new(ErrorKind::EvalError {
|
Self::new(ErrorKind::EvalError { msg, backtrace })
|
||||||
msg,
|
|
||||||
backtrace
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
pub fn internal(msg: String) -> Self {
|
pub fn internal(msg: String) -> Self {
|
||||||
Self::new(ErrorKind::InternalError(msg))
|
Self::new(ErrorKind::InternalError(msg))
|
||||||
|
|||||||
@@ -57,21 +57,17 @@ pub fn op_fetch_url(
|
|||||||
let cache = FetcherCache::new().map_err(|e| NixError::from(e.to_string()))?;
|
let cache = FetcherCache::new().map_err(|e| NixError::from(e.to_string()))?;
|
||||||
let downloader = Downloader::new();
|
let downloader = Downloader::new();
|
||||||
|
|
||||||
let file_name = name.unwrap_or_else(|| {
|
let file_name =
|
||||||
url.rsplit('/')
|
name.unwrap_or_else(|| url.rsplit('/').next().unwrap_or("download").to_string());
|
||||||
.next()
|
|
||||||
.unwrap_or("download")
|
|
||||||
.to_string()
|
|
||||||
});
|
|
||||||
|
|
||||||
if let Some(ref hash) = expected_hash {
|
if let Some(ref hash) = expected_hash
|
||||||
if let Some(cached) = cache.get_url(&url, hash) {
|
&& let Some(cached) = cache.get_url(&url, hash)
|
||||||
|
{
|
||||||
return Ok(FetchUrlResult {
|
return Ok(FetchUrlResult {
|
||||||
store_path: cached.to_string_lossy().to_string(),
|
store_path: cached.to_string_lossy().to_string(),
|
||||||
hash: hash.clone(),
|
hash: hash.clone(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
let data = downloader
|
let data = downloader
|
||||||
.download(&url)
|
.download(&url)
|
||||||
@@ -111,11 +107,6 @@ pub fn op_fetch_tarball(
|
|||||||
|
|
||||||
let dir_name = name.unwrap_or_else(|| "source".to_string());
|
let dir_name = name.unwrap_or_else(|| "source".to_string());
|
||||||
|
|
||||||
let is_nar_hash = expected_hash
|
|
||||||
.as_ref()
|
|
||||||
.map(|h| h.starts_with("sha256-"))
|
|
||||||
.unwrap_or(false);
|
|
||||||
|
|
||||||
if let Some(ref hash) = expected_hash {
|
if let Some(ref hash) = expected_hash {
|
||||||
let normalized = normalize_hash(hash);
|
let normalized = normalize_hash(hash);
|
||||||
if let Some(cached) = cache.get_tarball(&url, &normalized) {
|
if let Some(cached) = cache.get_tarball(&url, &normalized) {
|
||||||
@@ -134,17 +125,16 @@ pub fn op_fetch_tarball(
|
|||||||
let extracted_path = archive::extract_archive(&data, &temp_dir.path().to_path_buf())
|
let extracted_path = archive::extract_archive(&data, &temp_dir.path().to_path_buf())
|
||||||
.map_err(|e| NixError::from(e.to_string()))?;
|
.map_err(|e| NixError::from(e.to_string()))?;
|
||||||
|
|
||||||
let nar_hash = nar::compute_nar_hash(&extracted_path)
|
let nar_hash =
|
||||||
.map_err(|e| NixError::from(e.to_string()))?;
|
nar::compute_nar_hash(&extracted_path).map_err(|e| NixError::from(e.to_string()))?;
|
||||||
|
|
||||||
if let Some(ref expected) = expected_hash {
|
if let Some(ref expected) = expected_hash {
|
||||||
let normalized_expected = normalize_hash(expected);
|
let normalized_expected = normalize_hash(expected);
|
||||||
let hash_to_compare = if is_nar_hash { &nar_hash } else { &nar_hash };
|
|
||||||
|
|
||||||
if *hash_to_compare != normalized_expected {
|
if nar_hash != normalized_expected {
|
||||||
return Err(NixError::from(format!(
|
return Err(NixError::from(format!(
|
||||||
"hash mismatch for '{}': expected {}, got {}",
|
"hash mismatch for '{}': expected {}, got {}",
|
||||||
url, normalized_expected, hash_to_compare
|
url, normalized_expected, nar_hash
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -173,7 +163,16 @@ pub fn op_fetch_git(
|
|||||||
let cache = FetcherCache::new().map_err(|e| NixError::from(e.to_string()))?;
|
let cache = FetcherCache::new().map_err(|e| NixError::from(e.to_string()))?;
|
||||||
let dir_name = name.unwrap_or_else(|| "source".to_string());
|
let dir_name = name.unwrap_or_else(|| "source".to_string());
|
||||||
|
|
||||||
git::fetch_git(&cache, &url, git_ref.as_deref(), rev.as_deref(), shallow, submodules, all_refs, &dir_name)
|
git::fetch_git(
|
||||||
|
&cache,
|
||||||
|
&url,
|
||||||
|
git_ref.as_deref(),
|
||||||
|
rev.as_deref(),
|
||||||
|
shallow,
|
||||||
|
submodules,
|
||||||
|
all_refs,
|
||||||
|
&dir_name,
|
||||||
|
)
|
||||||
.map_err(|e| NixError::from(e.to_string()))
|
.map_err(|e| NixError::from(e.to_string()))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -187,18 +186,16 @@ pub fn op_fetch_hg(
|
|||||||
let cache = FetcherCache::new().map_err(|e| NixError::from(e.to_string()))?;
|
let cache = FetcherCache::new().map_err(|e| NixError::from(e.to_string()))?;
|
||||||
let dir_name = name.unwrap_or_else(|| "source".to_string());
|
let dir_name = name.unwrap_or_else(|| "source".to_string());
|
||||||
|
|
||||||
hg::fetch_hg(&cache, &url, rev.as_deref(), &dir_name)
|
hg::fetch_hg(&cache, &url, rev.as_deref(), &dir_name).map_err(|e| NixError::from(e.to_string()))
|
||||||
.map_err(|e| NixError::from(e.to_string()))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn normalize_hash(hash: &str) -> String {
|
fn normalize_hash(hash: &str) -> String {
|
||||||
if hash.starts_with("sha256-") {
|
if hash.starts_with("sha256-")
|
||||||
if let Some(b64) = hash.strip_prefix("sha256-") {
|
&& let Some(b64) = hash.strip_prefix("sha256-")
|
||||||
if let Ok(bytes) = base64_decode(b64) {
|
&& let Ok(bytes) = base64_decode(b64)
|
||||||
|
{
|
||||||
return hex::encode(bytes);
|
return hex::encode(bytes);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
hash.to_string()
|
hash.to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -212,7 +209,9 @@ fn base64_decode(input: &str) -> Result<Vec<u8>, String> {
|
|||||||
let mut bits = 0;
|
let mut bits = 0;
|
||||||
|
|
||||||
for c in input.bytes() {
|
for c in input.bytes() {
|
||||||
let value = ALPHABET.iter().position(|&x| x == c)
|
let value = ALPHABET
|
||||||
|
.iter()
|
||||||
|
.position(|&x| x == c)
|
||||||
.ok_or_else(|| format!("Invalid base64 character: {}", c as char))?;
|
.ok_or_else(|| format!("Invalid base64 character: {}", c as char))?;
|
||||||
|
|
||||||
buffer = (buffer << 6) | (value as u32);
|
buffer = (buffer << 6) | (value as u32);
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use std::fs::{self, File};
|
use std::fs::{self, File};
|
||||||
use std::io::Cursor;
|
use std::io::Cursor;
|
||||||
use std::path::PathBuf;
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
use flate2::read::GzDecoder;
|
use flate2::read::GzDecoder;
|
||||||
|
|
||||||
@@ -48,7 +48,7 @@ impl ArchiveFormat {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn extract_archive(data: &[u8], dest: &PathBuf) -> Result<PathBuf, ArchiveError> {
|
pub fn extract_archive(data: &[u8], dest: &Path) -> Result<PathBuf, ArchiveError> {
|
||||||
let format = ArchiveFormat::detect("", data);
|
let format = ArchiveFormat::detect("", data);
|
||||||
|
|
||||||
let temp_dir = dest.join("_extract_temp");
|
let temp_dir = dest.join("_extract_temp");
|
||||||
@@ -65,34 +65,34 @@ pub fn extract_archive(data: &[u8], dest: &PathBuf) -> Result<PathBuf, ArchiveEr
|
|||||||
strip_single_toplevel(&temp_dir, dest)
|
strip_single_toplevel(&temp_dir, dest)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn extract_tar_gz(data: &[u8], dest: &PathBuf) -> Result<(), ArchiveError> {
|
fn extract_tar_gz(data: &[u8], dest: &Path) -> Result<(), ArchiveError> {
|
||||||
let decoder = GzDecoder::new(Cursor::new(data));
|
let decoder = GzDecoder::new(Cursor::new(data));
|
||||||
let mut archive = tar::Archive::new(decoder);
|
let mut archive = tar::Archive::new(decoder);
|
||||||
archive.unpack(dest)?;
|
archive.unpack(dest)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn extract_tar_xz(data: &[u8], dest: &PathBuf) -> Result<(), ArchiveError> {
|
fn extract_tar_xz(data: &[u8], dest: &Path) -> Result<(), ArchiveError> {
|
||||||
let decoder = xz2::read::XzDecoder::new(Cursor::new(data));
|
let decoder = xz2::read::XzDecoder::new(Cursor::new(data));
|
||||||
let mut archive = tar::Archive::new(decoder);
|
let mut archive = tar::Archive::new(decoder);
|
||||||
archive.unpack(dest)?;
|
archive.unpack(dest)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn extract_tar_bz2(data: &[u8], dest: &PathBuf) -> Result<(), ArchiveError> {
|
fn extract_tar_bz2(data: &[u8], dest: &Path) -> Result<(), ArchiveError> {
|
||||||
let decoder = bzip2::read::BzDecoder::new(Cursor::new(data));
|
let decoder = bzip2::read::BzDecoder::new(Cursor::new(data));
|
||||||
let mut archive = tar::Archive::new(decoder);
|
let mut archive = tar::Archive::new(decoder);
|
||||||
archive.unpack(dest)?;
|
archive.unpack(dest)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn extract_tar(data: &[u8], dest: &PathBuf) -> Result<(), ArchiveError> {
|
fn extract_tar(data: &[u8], dest: &Path) -> Result<(), ArchiveError> {
|
||||||
let mut archive = tar::Archive::new(Cursor::new(data));
|
let mut archive = tar::Archive::new(Cursor::new(data));
|
||||||
archive.unpack(dest)?;
|
archive.unpack(dest)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn extract_zip(data: &[u8], dest: &PathBuf) -> Result<(), ArchiveError> {
|
fn extract_zip(data: &[u8], dest: &Path) -> Result<(), ArchiveError> {
|
||||||
let cursor = Cursor::new(data);
|
let cursor = Cursor::new(data);
|
||||||
let mut archive = zip::ZipArchive::new(cursor)?;
|
let mut archive = zip::ZipArchive::new(cursor)?;
|
||||||
|
|
||||||
@@ -122,7 +122,7 @@ fn extract_zip(data: &[u8], dest: &PathBuf) -> Result<(), ArchiveError> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn strip_single_toplevel(temp_dir: &PathBuf, dest: &PathBuf) -> Result<PathBuf, ArchiveError> {
|
fn strip_single_toplevel(temp_dir: &Path, dest: &Path) -> Result<PathBuf, ArchiveError> {
|
||||||
let entries: Vec<_> = fs::read_dir(temp_dir)?
|
let entries: Vec<_> = fs::read_dir(temp_dir)?
|
||||||
.filter_map(|e| e.ok())
|
.filter_map(|e| e.ok())
|
||||||
.filter(|e| !e.file_name().to_string_lossy().starts_with('.'))
|
.filter(|e| !e.file_name().to_string_lossy().starts_with('.'))
|
||||||
@@ -131,7 +131,7 @@ fn strip_single_toplevel(temp_dir: &PathBuf, dest: &PathBuf) -> Result<PathBuf,
|
|||||||
let source_dir = if entries.len() == 1 && entries[0].file_type()?.is_dir() {
|
let source_dir = if entries.len() == 1 && entries[0].file_type()?.is_dir() {
|
||||||
entries[0].path()
|
entries[0].path()
|
||||||
} else {
|
} else {
|
||||||
temp_dir.clone()
|
temp_dir.to_path_buf()
|
||||||
};
|
};
|
||||||
|
|
||||||
let final_dest = dest.join("content");
|
let final_dest = dest.join("content");
|
||||||
@@ -149,7 +149,7 @@ fn strip_single_toplevel(temp_dir: &PathBuf, dest: &PathBuf) -> Result<PathBuf,
|
|||||||
Ok(final_dest)
|
Ok(final_dest)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn copy_dir_recursive(src: &PathBuf, dst: &PathBuf) -> Result<(), std::io::Error> {
|
fn copy_dir_recursive(src: &Path, dst: &Path) -> Result<(), std::io::Error> {
|
||||||
fs::create_dir_all(dst)?;
|
fs::create_dir_all(dst)?;
|
||||||
|
|
||||||
for entry in fs::read_dir(src)? {
|
for entry in fs::read_dir(src)? {
|
||||||
@@ -194,7 +194,9 @@ impl std::fmt::Display for ArchiveError {
|
|||||||
match self {
|
match self {
|
||||||
ArchiveError::IoError(e) => write!(f, "I/O error: {}", e),
|
ArchiveError::IoError(e) => write!(f, "I/O error: {}", e),
|
||||||
ArchiveError::ZipError(e) => write!(f, "ZIP error: {}", e),
|
ArchiveError::ZipError(e) => write!(f, "ZIP error: {}", e),
|
||||||
ArchiveError::UnsupportedFormat(fmt) => write!(f, "Unsupported archive format: {}", fmt),
|
ArchiveError::UnsupportedFormat(fmt) => {
|
||||||
|
write!(f, "Unsupported archive format: {}", fmt)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -97,7 +97,8 @@ impl FetcherCache {
|
|||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let meta: CacheMetadata = serde_json::from_str(&fs::read_to_string(&meta_path).ok()?).ok()?;
|
let meta: CacheMetadata =
|
||||||
|
serde_json::from_str(&fs::read_to_string(&meta_path).ok()?).ok()?;
|
||||||
|
|
||||||
if meta.hash == expected_hash {
|
if meta.hash == expected_hash {
|
||||||
Some(data_path)
|
Some(data_path)
|
||||||
@@ -166,7 +167,8 @@ impl FetcherCache {
|
|||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let meta: CacheMetadata = serde_json::from_str(&fs::read_to_string(&meta_path).ok()?).ok()?;
|
let meta: CacheMetadata =
|
||||||
|
serde_json::from_str(&fs::read_to_string(&meta_path).ok()?).ok()?;
|
||||||
|
|
||||||
if meta.hash == expected_hash {
|
if meta.hash == expected_hash {
|
||||||
Some(self.make_store_path(&meta.hash, &meta.name))
|
Some(self.make_store_path(&meta.hash, &meta.name))
|
||||||
|
|||||||
@@ -2,8 +2,8 @@ use std::fs;
|
|||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
|
|
||||||
use super::cache::FetcherCache;
|
|
||||||
use super::FetchGitResult;
|
use super::FetchGitResult;
|
||||||
|
use super::cache::FetcherCache;
|
||||||
|
|
||||||
pub fn fetch_git(
|
pub fn fetch_git(
|
||||||
cache: &FetcherCache,
|
cache: &FetcherCache,
|
||||||
@@ -72,10 +72,7 @@ fn fetch_repo(repo: &PathBuf, all_refs: bool) -> Result<(), GitError> {
|
|||||||
args.push("--all");
|
args.push("--all");
|
||||||
}
|
}
|
||||||
|
|
||||||
let output = Command::new("git")
|
let output = Command::new("git").args(args).current_dir(repo).output()?;
|
||||||
.args(args)
|
|
||||||
.current_dir(repo)
|
|
||||||
.output()?;
|
|
||||||
|
|
||||||
if !output.status.success() {
|
if !output.status.success() {
|
||||||
return Err(GitError::CommandFailed {
|
return Err(GitError::CommandFailed {
|
||||||
@@ -87,7 +84,11 @@ fn fetch_repo(repo: &PathBuf, all_refs: bool) -> Result<(), GitError> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resolve_rev(repo: &PathBuf, git_ref: Option<&str>, rev: Option<&str>) -> Result<String, GitError> {
|
fn resolve_rev(
|
||||||
|
repo: &PathBuf,
|
||||||
|
git_ref: Option<&str>,
|
||||||
|
rev: Option<&str>,
|
||||||
|
) -> Result<String, GitError> {
|
||||||
if let Some(rev) = rev {
|
if let Some(rev) = rev {
|
||||||
return Ok(rev.to_string());
|
return Ok(rev.to_string());
|
||||||
}
|
}
|
||||||
@@ -263,7 +264,7 @@ fn days_to_ymd(days: u64) -> (u64, u64, u64) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn is_leap_year(y: u64) -> bool {
|
fn is_leap_year(y: u64) -> bool {
|
||||||
(y % 4 == 0 && y % 100 != 0) || (y % 400 == 0)
|
(y.is_multiple_of(4) && !y.is_multiple_of(100)) || y.is_multiple_of(400)
|
||||||
}
|
}
|
||||||
|
|
||||||
trait Pipe: Sized {
|
trait Pipe: Sized {
|
||||||
|
|||||||
@@ -2,8 +2,8 @@ use std::fs;
|
|||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
|
|
||||||
use super::cache::FetcherCache;
|
|
||||||
use super::FetchHgResult;
|
use super::FetchHgResult;
|
||||||
|
use super::cache::FetcherCache;
|
||||||
|
|
||||||
pub fn fetch_hg(
|
pub fn fetch_hg(
|
||||||
cache: &FetcherCache,
|
cache: &FetcherCache,
|
||||||
|
|||||||
Reference in New Issue
Block a user