Skip to content

Commit d60a681

Browse files
Use argument array persistence even for single arg filters
1 parent 38c41df commit d60a681

File tree

2 files changed

+227
-142
lines changed

2 files changed

+227
-142
lines changed

josh-core/src/filter/persist.rs

Lines changed: 146 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -190,52 +190,45 @@ impl InMemoryBuilder {
190190
push_tree_entries(&mut entries, [("chain", params_tree)]);
191191
}
192192
Op::Exclude(b) => {
193-
let child = self.build_filter(*b)?;
194-
push_tree_entries(&mut entries, [("exclude", child)]);
193+
let params_tree = self.build_filter_params(&[*b])?;
194+
push_tree_entries(&mut entries, [("exclude", params_tree)]);
195195
}
196196
Op::Pin(b) => {
197-
let child = self.build_filter(*b)?;
198-
push_tree_entries(&mut entries, [("pin", child)]);
197+
let params_tree = self.build_filter_params(&[*b])?;
198+
push_tree_entries(&mut entries, [("pin", params_tree)]);
199199
}
200200
Op::Subdir(path) => {
201-
let blob = self.write_blob(path.to_string_lossy().as_bytes());
202-
push_blob_entries(&mut entries, [("subdir", blob)]);
201+
let params_tree = self.build_str_params(&[path.to_string_lossy().as_ref()]);
202+
push_tree_entries(&mut entries, [("subdir", params_tree)]);
203203
}
204204
Op::Prefix(path) => {
205-
let blob = self.write_blob(path.to_string_lossy().as_bytes());
206-
push_blob_entries(&mut entries, [("prefix", blob)]);
205+
let params_tree = self.build_str_params(&[path.to_string_lossy().as_ref()]);
206+
push_tree_entries(&mut entries, [("prefix", params_tree)]);
207207
}
208208
Op::File(dest_path, source_path) => {
209-
if source_path == dest_path {
210-
// Backward compatibility: use blob format when source and dest are the same
211-
let blob = self.write_blob(dest_path.to_string_lossy().as_bytes());
212-
push_blob_entries(&mut entries, [("file", blob)]);
213-
} else {
214-
// New format: use tree format when source and dest differ
215-
// Store as (dest_path, source_path) to match enum order
216-
let params_tree = self.build_str_params(&[
217-
dest_path.to_string_lossy().as_ref(),
218-
source_path.to_string_lossy().as_ref(),
219-
]);
220-
push_tree_entries(&mut entries, [("file", params_tree)]);
221-
}
209+
// Store as (dest_path, source_path) to match enum order
210+
let params_tree = self.build_str_params(&[
211+
dest_path.to_string_lossy().as_ref(),
212+
source_path.to_string_lossy().as_ref(),
213+
]);
214+
push_tree_entries(&mut entries, [("file", params_tree)]);
222215
}
223216
#[cfg(feature = "incubating")]
224217
Op::Embed(path) => {
225-
let blob = self.write_blob(path.to_string_lossy().as_bytes());
226-
push_blob_entries(&mut entries, [("embed", blob)]);
218+
let params_tree = self.build_str_params(&[path.to_string_lossy().as_ref()]);
219+
push_tree_entries(&mut entries, [("embed", params_tree)]);
227220
}
228221
Op::Pattern(pattern) => {
229-
let blob = self.write_blob(pattern.as_bytes());
230-
push_blob_entries(&mut entries, [("pattern", blob)]);
222+
let params_tree = self.build_str_params(&[pattern.as_ref()]);
223+
push_tree_entries(&mut entries, [("pattern", params_tree)]);
231224
}
232225
Op::Workspace(path) => {
233-
let blob = self.write_blob(path.to_string_lossy().as_bytes());
234-
push_blob_entries(&mut entries, [("workspace", blob)]);
226+
let params_tree = self.build_str_params(&[path.to_string_lossy().as_ref()]);
227+
push_tree_entries(&mut entries, [("workspace", params_tree)]);
235228
}
236229
Op::Stored(path) => {
237-
let blob = self.write_blob(path.to_string_lossy().as_bytes());
238-
push_blob_entries(&mut entries, [("stored", blob)]);
230+
let params_tree = self.build_str_params(&[path.to_string_lossy().as_ref()]);
231+
push_tree_entries(&mut entries, [("stored", params_tree)]);
239232
}
240233
Op::Nop => {
241234
let blob = self.write_blob(b"");
@@ -256,13 +249,13 @@ impl InMemoryBuilder {
256249
}
257250
#[cfg(feature = "incubating")]
258251
Op::Link(mode) => {
259-
let blob = self.write_blob(mode.as_bytes());
260-
push_blob_entries(&mut entries, [("link", blob)]);
252+
let params_tree = self.build_str_params(&[mode.as_ref()]);
253+
push_tree_entries(&mut entries, [("link", params_tree)]);
261254
}
262255
#[cfg(feature = "incubating")]
263256
Op::Adapt(mode) => {
264-
let blob = self.write_blob(mode.as_bytes());
265-
push_blob_entries(&mut entries, [("adapt", blob)]);
257+
let params_tree = self.build_str_params(&[mode.as_ref()]);
258+
push_tree_entries(&mut entries, [("adapt", params_tree)]);
266259
}
267260
#[cfg(feature = "incubating")]
268261
Op::Unlink => {
@@ -329,8 +322,8 @@ impl InMemoryBuilder {
329322
push_tree_entries(&mut entries, [("regex_replace", params_tree)]);
330323
}
331324
Op::Hook(hook) => {
332-
let blob = self.write_blob(hook.as_bytes());
333-
push_blob_entries(&mut entries, [("hook", blob)]);
325+
let params_tree = self.build_str_params(&[hook.as_ref()]);
326+
push_tree_entries(&mut entries, [("hook", params_tree)]);
334327
}
335328
}
336329

@@ -400,13 +393,29 @@ fn from_tree2(repo: &git2::Repository, tree_oid: git2::Oid) -> JoshResult<Op> {
400393
}
401394
#[cfg(feature = "incubating")]
402395
"link" => {
403-
let blob = repo.find_blob(entry.id())?;
404-
Ok(Op::Link(std::str::from_utf8(blob.content())?.to_string()))
396+
let inner = repo.find_tree(entry.id())?;
397+
let mode_blob = repo.find_blob(
398+
inner
399+
.get_name("0")
400+
.ok_or_else(|| josh_error("link: missing mode"))?
401+
.id(),
402+
)?;
403+
Ok(Op::Link(
404+
std::str::from_utf8(mode_blob.content())?.to_string(),
405+
))
405406
}
406407
#[cfg(feature = "incubating")]
407408
"adapt" => {
408-
let blob = repo.find_blob(entry.id())?;
409-
Ok(Op::Adapt(std::str::from_utf8(blob.content())?.to_string()))
409+
let inner = repo.find_tree(entry.id())?;
410+
let mode_blob = repo.find_blob(
411+
inner
412+
.get_name("0")
413+
.ok_or_else(|| josh_error("adapt: missing mode"))?
414+
.id(),
415+
)?;
416+
Ok(Op::Adapt(
417+
std::str::from_utf8(mode_blob.content())?.to_string(),
418+
))
410419
}
411420
#[cfg(feature = "incubating")]
412421
"unlink" => {
@@ -443,8 +452,14 @@ fn from_tree2(repo: &git2::Repository, tree_oid: git2::Oid) -> JoshResult<Op> {
443452
}
444453
}
445454
"hook" => {
446-
let blob = repo.find_blob(entry.id())?;
447-
let hook_name = std::str::from_utf8(blob.content())?.to_string();
455+
let inner = repo.find_tree(entry.id())?;
456+
let hook_blob = repo.find_blob(
457+
inner
458+
.get_name("0")
459+
.ok_or_else(|| josh_error("hook: missing hook name"))?
460+
.id(),
461+
)?;
462+
let hook_name = std::str::from_utf8(hook_blob.content())?.to_string();
448463
Ok(Op::Hook(hook_name))
449464
}
450465
"author" => {
@@ -504,67 +519,91 @@ fn from_tree2(repo: &git2::Repository, tree_oid: git2::Oid) -> JoshResult<Op> {
504519
Ok(Op::Message(fmt, regex))
505520
}
506521
"subdir" => {
507-
let blob = repo.find_blob(entry.id())?;
508-
let path = std::str::from_utf8(blob.content())?;
522+
let inner = repo.find_tree(entry.id())?;
523+
let path_blob = repo.find_blob(
524+
inner
525+
.get_name("0")
526+
.ok_or_else(|| josh_error("subdir: missing path"))?
527+
.id(),
528+
)?;
529+
let path = std::str::from_utf8(path_blob.content())?;
509530
Ok(Op::Subdir(std::path::PathBuf::from(path)))
510531
}
511532
"prefix" => {
512-
let blob = repo.find_blob(entry.id())?;
513-
let path = std::str::from_utf8(blob.content())?;
533+
let inner = repo.find_tree(entry.id())?;
534+
let path_blob = repo.find_blob(
535+
inner
536+
.get_name("0")
537+
.ok_or_else(|| josh_error("prefix: missing path"))?
538+
.id(),
539+
)?;
540+
let path = std::str::from_utf8(path_blob.content())?;
514541
Ok(Op::Prefix(std::path::PathBuf::from(path)))
515542
}
516543
"file" => {
517-
// Try to read as tree (new format with destination path)
518-
if let Ok(inner) = repo.find_tree(entry.id()) {
519-
let dest_blob = repo.find_blob(
520-
inner
521-
.get_name("0")
522-
.ok_or_else(|| josh_error("file: missing destination path"))?
523-
.id(),
524-
)?;
525-
let dest_path_str = std::str::from_utf8(dest_blob.content())?.to_string();
526-
let source_path = inner
544+
let inner = repo.find_tree(entry.id())?;
545+
let dest_blob = repo.find_blob(
546+
inner
547+
.get_name("0")
548+
.ok_or_else(|| josh_error("file: missing destination path"))?
549+
.id(),
550+
)?;
551+
let source_blob = repo.find_blob(
552+
inner
527553
.get_name("1")
528-
.and_then(|entry| repo.find_blob(entry.id()).ok())
529-
.and_then(|blob| {
530-
std::str::from_utf8(blob.content())
531-
.ok()
532-
.map(|s| s.to_string())
533-
})
534-
.map(|s| std::path::PathBuf::from(s))
535-
.unwrap_or_else(|| std::path::PathBuf::from(&dest_path_str));
536-
Ok(Op::File(
537-
std::path::PathBuf::from(dest_path_str),
538-
source_path,
539-
))
540-
} else {
541-
// Fall back to blob format (old format, backward compatibility)
542-
let blob = repo.find_blob(entry.id())?;
543-
let path_str = std::str::from_utf8(blob.content())?.to_string();
544-
let path_buf = std::path::PathBuf::from(&path_str);
545-
// When reading from blob format, destination is the same as source
546-
Ok(Op::File(path_buf.clone(), path_buf))
547-
}
554+
.ok_or_else(|| josh_error("file: missing source path"))?
555+
.id(),
556+
)?;
557+
let dest_path_str = std::str::from_utf8(dest_blob.content())?.to_string();
558+
let source_path_str = std::str::from_utf8(source_blob.content())?.to_string();
559+
Ok(Op::File(
560+
std::path::PathBuf::from(dest_path_str),
561+
std::path::PathBuf::from(source_path_str),
562+
))
548563
}
549564
#[cfg(feature = "incubating")]
550565
"embed" => {
551-
let blob = repo.find_blob(entry.id())?;
552-
let path = std::str::from_utf8(blob.content())?;
566+
let inner = repo.find_tree(entry.id())?;
567+
let path_blob = repo.find_blob(
568+
inner
569+
.get_name("0")
570+
.ok_or_else(|| josh_error("embed: missing path"))?
571+
.id(),
572+
)?;
573+
let path = std::str::from_utf8(path_blob.content())?;
553574
Ok(Op::Embed(std::path::PathBuf::from(path)))
554575
}
555576
"pattern" => {
556-
let blob = repo.find_blob(entry.id())?;
557-
let pattern = std::str::from_utf8(blob.content())?.to_string();
577+
let inner = repo.find_tree(entry.id())?;
578+
let pattern_blob = repo.find_blob(
579+
inner
580+
.get_name("0")
581+
.ok_or_else(|| josh_error("pattern: missing pattern"))?
582+
.id(),
583+
)?;
584+
let pattern = std::str::from_utf8(pattern_blob.content())?.to_string();
558585
Ok(Op::Pattern(pattern))
559586
}
560587
"workspace" => {
561-
let blob = repo.find_blob(entry.id())?;
562-
let path = std::str::from_utf8(blob.content())?;
588+
let inner = repo.find_tree(entry.id())?;
589+
let path_blob = repo.find_blob(
590+
inner
591+
.get_name("0")
592+
.ok_or_else(|| josh_error("workspace: missing path"))?
593+
.id(),
594+
)?;
595+
let path = std::str::from_utf8(path_blob.content())?;
563596
Ok(Op::Workspace(std::path::PathBuf::from(path)))
564597
}
565598
"stored" => {
566-
let blob = repo.find_blob(entry.id())?;
567-
let path = std::str::from_utf8(blob.content())?;
599+
let inner = repo.find_tree(entry.id())?;
600+
let path_blob = repo.find_blob(
601+
inner
602+
.get_name("0")
603+
.ok_or_else(|| josh_error("stored: missing path"))?
604+
.id(),
605+
)?;
606+
let path = std::str::from_utf8(path_blob.content())?;
568607
Ok(Op::Stored(std::path::PathBuf::from(path)))
569608
}
570609
"compose" => {
@@ -626,13 +665,33 @@ fn from_tree2(repo: &git2::Repository, tree_oid: git2::Oid) -> JoshResult<Op> {
626665
}
627666
"exclude" => {
628667
let exclude_tree = repo.find_tree(entry.id())?;
629-
let filter = from_tree2(repo, exclude_tree.id())?;
630-
Ok(Op::Exclude(to_filter(filter)))
668+
if exclude_tree.len() == 1 {
669+
let filter_tree = repo.find_tree(
670+
exclude_tree
671+
.get_name("0")
672+
.ok_or_else(|| josh_error("exclude: missing 0"))?
673+
.id(),
674+
)?;
675+
let filter = from_tree2(repo, filter_tree.id())?;
676+
Ok(Op::Exclude(to_filter(filter)))
677+
} else {
678+
Err(josh_error("exclude: expected 1 entry"))
679+
}
631680
}
632681
"pin" => {
633682
let pin_tree = repo.find_tree(entry.id())?;
634-
let filter = from_tree2(repo, pin_tree.id())?;
635-
Ok(Op::Pin(to_filter(filter)))
683+
if pin_tree.len() == 1 {
684+
let filter_tree = repo.find_tree(
685+
pin_tree
686+
.get_name("0")
687+
.ok_or_else(|| josh_error("pin: missing 0"))?
688+
.id(),
689+
)?;
690+
let filter = from_tree2(repo, filter_tree.id())?;
691+
Ok(Op::Pin(to_filter(filter)))
692+
} else {
693+
Err(josh_error("pin: expected 1 entry"))
694+
}
636695
}
637696
"rev" => {
638697
let rev_tree = repo.find_tree(entry.id())?;

0 commit comments

Comments
 (0)