///|
priv struct FindBatch {
command : Array[String]
values : Array[String]
base : Int64
copies : Int64
limit : Int64
mut bytes : Int64
}
///|
fn FindBatch::new(
command : Array[String],
limit~ : Int64 = 65536L,
) -> FindBatch {
let mut base = 0L
let mut copies = 0L
for argument in command {
if argument == "{}" {
copies += 1L
} else {
base += @process.argument_bytes(argument)
}
}
{ command, values: [], base, copies, limit, bytes: base, }
}
///|
async fn execute_find_batch(argv : Array[String]) -> Bool {
guard argv.get(0) is Some(program) else { return false }
let code = @process.run(@process.child(program, argv[1:].to_owned())) catch {
error => {
@stdio.stderr.write("find: \{error}\n")
return false
}
}
code == 0
}
///|
async fn FindBatch::flush(
self : FindBatch,
execute~ : async (Array[String]) -> Bool = execute_find_batch,
) -> Bool {
guard !self.values.is_empty() else { return true }
let argv : Array[String] = []
for argument in self.command {
if argument == "{}" {
argv.append(self.values)
} else {
argv.push(argument)
}
}
// Consume the batch before execution: a failed invocation is never replayed.
self.values.clear()
self.bytes = self.base
execute(argv)
}
///|
async fn FindBatch::push(
self : FindBatch,
path : String,
execute~ : async (Array[String]) -> Bool = execute_find_batch,
) -> Bool {
let cost = @process.argument_bytes(path) * self.copies
if self.base + cost > self.limit {
@stdio.stderr.write(
"find: argument exceeds \{self.limit} byte execution limit: '\{path}'\n",
)
return false
}
let succeeded = if self.bytes + cost > self.limit {
self.flush(execute~)
} else {
true
}
self.values.push(path)
self.bytes += cost
succeeded
}