/examples/async-file-report/report.nct
report.nct
see ./index.nct
use std/fs.FileType
use std/fs
use std/io.{File, Writer}
use std/io/buffer.BufWriter
use std/io/stream.ByteChunks
use std/iter/asynchronous.AsyncIterator
use std/process
use std/string.String
use std/task
use std/task.Timeout
use std/time.Duration
const CHUNK_BYTES: usize = 8192
const OUTPUT_BUFFER_BYTES: usize = 4096
const REPORT_TIMEOUT_SECONDS: u64 = 5
async func count_file(path: &str): usize! {
let source = await File.open(path)?
var chunks = ByteChunks.with_chunk_size(move source, CHUNK_BYTES)
var total: usize = 0
while true {
let chunk = await chunks.next()? otherwise { break }
total += chunk.len()
}
var completed = chunks.finish()
await completed.close()?
return total
}
async func write_summary<W>(
output: &+W,
files: usize,
bytes: usize,
): void! where W impl Writer {
let file_text = files.to_string()
let byte_text = bytes.to_string()
await output.write_text("files")?
await output.write_text("\t")?
await output.write_line(&file_text)?
await output.write_text("bytes")?
await output.write_text("\t")?
await output.write_line(&byte_text)?
return
}
async func build_report(root: &str, temporary_path: &str, output_path: &str): void! {
let walker = await fs.walk_dir(root)?
let files = walker.filter(
(entry) {
if entry.file_type() is FileType.regular { return true }
return false
},
)
let indexed = files.enumerate()
var files_seen: usize = 0
var bytes_seen: usize = 0
for await record in move indexed {
files_seen = record.index + 1
let entry = move record.item
let path: &str = entry.path()
bytes_seen += await count_file(path)?
}
let destination = await File.create(temporary_path)?
var output = BufWriter.with_capacity(move destination, OUTPUT_BUFFER_BYTES)
await write_summary(&+output, files_seen, bytes_seen)?
var completed = await output.finish()?
await completed.close()?
await fs.rename(temporary_path, output_path)?
return
}
async func remove_temporary(path: &str): void {
await fs.remove_file(path) catch _ {}
return
}
async func run(): i32 {
if process.arg_count() != 3 {
return 2
}
let root = process.arg(1) catch _ { return 2 } otherwise { return 2 }
let output_path = process.arg(2) catch _ { return 2 } otherwise { return 2 }
let temporary_path = String.concat(output_path, ".tmp")
let bounded = await task.with_timeout(
build_report(root, &temporary_path, output_path),
Duration.from_seconds(REPORT_TIMEOUT_SECONDS),
)
match move bounded {
Timeout.completed(result) {
move result catch _ {
await remove_temporary(&temporary_path)
return 1
}
return 0
}
Timeout.elapsed {
await remove_temporary(&temporary_path)
return 1
}
}
}