Skip to content

Commit

Permalink
AHA!
Browse files Browse the repository at this point in the history
  • Loading branch information
jessekrubin committed Jan 18, 2025
1 parent 6e31a30 commit 8e9a503
Show file tree
Hide file tree
Showing 6 changed files with 87 additions and 53 deletions.
10 changes: 5 additions & 5 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ members = [
]

[workspace.package]
version = "0.7.2"
version = "0.7.3"
authors = [
"Jesse Rubin <[email protected]>",
"Dan Costello <[email protected]>",
Expand Down
86 changes: 40 additions & 46 deletions crates/utiles/src/cli/commands/rimraf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,29 +2,25 @@ use futures::stream::{self, StreamExt};
use jiff::SignedDuration;
use std::collections::BTreeMap;
use std::path::{Path, PathBuf};
use std::sync::{
atomic::{AtomicU64, Ordering},
Arc,
};
use std::time::Duration;
use tokio::{
fs,
sync::mpsc::{self, UnboundedReceiver, UnboundedSender},
task::JoinHandle,
};
use tracing::{error, info, trace};
use walkdir::{DirEntry, WalkDir};
use walkdir::WalkDir;

use crate::cli::args::RimrafArgs;
use crate::errors::UtilesResult;
use crate::UtilesError;

/// Represents different ways we might update stats.
/// stat update thing
#[derive(Debug)]
enum StatsEvent {
/// A file was removed. The `u64` is the file size in bytes.
/// A file was removed (contains size)
FileRemoved(u64),
/// A directory was removed.
/// dir removed
DirRemoved,
}

Expand Down Expand Up @@ -54,41 +50,41 @@ impl FinalRimrafStats {
let ndirs = self.stats.ndirs;
let nbytes = self.stats.nbytes;
let signed_duration = SignedDuration::try_from(self.elapsed);
let elapsed_str = signed_duration
.map(|sd| {
let s = format!("{:#}", sd);
s
})
.unwrap_or_else(|_| "unknown".to_string());
info!(
"NUKED: nfiles={nfiles}, ndirs={ndirs}, nbytes={nbytes} in {elapsed_str}"
);
match signed_duration {
Ok(sd) => {
info!(
"NUKED: nfiles={nfiles}, ndirs={ndirs}, nbytes={nbytes} in {sd:#}"
);
}
Err(e) => {
trace!("Failed to convert Duration to SignedDuration: {:?}", e);
info!("NUKED: nfiles={nfiles}, ndirs={ndirs}, nbytes={nbytes}");
}
}
}

pub fn json_str(&self) -> String {
let nfiles = self.stats.nfiles;
let ndirs = self.stats.ndirs;
let nbytes = self.stats.nbytes;
let signed_duration = SignedDuration::try_from(self.elapsed);
let elapsed_str = signed_duration
.map(|sd| {
let s = format!("{:#}", sd);
s
})
.unwrap_or_else(|_| "unknown".to_string());
let json_str = format!(
r#"{{"nfiles":{nfiles},"ndirs":{ndirs},"nbytes":{nbytes},"elapsed":"{elapsed_str}"}}"#,
nfiles = nfiles,
ndirs = ndirs,
nbytes = nbytes,
elapsed_str = elapsed_str,
);
json_str
match signed_duration {
Ok(sd) => {
format!(
r#"{{"nfiles": {nfiles}, "ndirs": {ndirs}, "nbytes": {nbytes}, "elapsed": "{sd:#}"}}"#
)
}
Err(_e) => {
format!(
r#"{{"nfiles": {nfiles}, "ndirs": {ndirs}, "nbytes": {nbytes}, "elapsed": null}}"#
)
}
}
}
}

/// A separate task that collects stats events and updates `RimrafStats`.
async fn stats_collector(mut rx: UnboundedReceiver<StatsEvent>) -> (FinalRimrafStats) {
async fn stats_collector(mut rx: UnboundedReceiver<StatsEvent>) -> FinalRimrafStats {
let mut stats = RimrafStats::default();
let start = std::time::Instant::now();
while let Some(event) = rx.recv().await {
Expand Down Expand Up @@ -141,10 +137,12 @@ async fn remove_all_files(
}
};

if !cfg.dryrun {
if cfg.dryrun {
let _ = tx.send(StatsEvent::FileRemoved(fsize));
} else {
// Remove file
match fs::remove_file(&path).await {
Ok(_) => {
Ok(()) => {
// Attempt to re-check size
match path.metadata() {
Ok(meta) => {
Expand All @@ -160,9 +158,6 @@ async fn remove_all_files(
error!("Removing file {:?} failed: {:?}", path, e);
}
}
} else {
// Just gather size
let _ = tx.send(StatsEvent::FileRemoved(fsize));
}
}
})
Expand All @@ -184,14 +179,12 @@ async fn remove_all_directories_in_stages(
let mut depth_map: BTreeMap<usize, Vec<PathBuf>> = BTreeMap::new();

// build the depth map
for entry_result in WalkDir::new(dirpath) {
if let Ok(entry) = entry_result {
if entry.file_type().is_dir() {
// dirs.push(entry);
let path = entry.path().to_owned();
let depth = path.components().count(); // number of components
depth_map.entry(depth).or_default().push(path);
}
for entry in WalkDir::new(dirpath).into_iter().flatten() {
if entry.file_type().is_dir() {
// dirs.push(entry);
let path = entry.path().to_owned();
let depth = path.components().count(); // number of components
depth_map.entry(depth).or_default().push(path);
}
}

Expand All @@ -206,7 +199,7 @@ async fn remove_all_directories_in_stages(
let tx = tx.clone();
async move {
match fs::remove_dir(&path).await {
Ok(_) => {
Ok(()) => {
// Send DirRemoved event
let _ = tx.send(StatsEvent::DirRemoved);
}
Expand Down Expand Up @@ -266,6 +259,7 @@ pub async fn rimraf_main(args: RimrafArgs) -> UtilesResult<()> {
// let (nfiles, ndirs, nbytes) = final_stats.snapshot();
if args.verbose {
final_stats.log();
println!("{}", final_stats.json_str());
}
Ok(())
}
4 changes: 3 additions & 1 deletion crates/utiles/src/copy/pyramid.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,9 @@ pub async fn copy_mbtiles2fs(cfg: &CopyConfig) -> UtilesResult<()> {
let output_dir = Path::new(&cfg.dst);
let mbt = Mbtiles::open_existing(mbt_path)?;
let where_clause = cfg.mbtiles_sql_where()?;
info!("where_clause: {where_clause:?}");
if !where_clause.is_empty() {
debug!("where_clause: {where_clause:?}");
}
let start_time = std::time::Instant::now();

// let count_query = &"SELECT count(*) FROM tiles".to_string();
Expand Down
Empty file.
38 changes: 38 additions & 0 deletions utiles-pyo3/tests/cli/test_rimraf.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
"""Utiles rust cli rimraf tests"""

from __future__ import annotations

from pathlib import Path

from utiles.dev.testing import run_cli as _run_cli


def _osm_standard_z0z4_mbtiles(test_data: Path) -> Path:
return test_data / "mbtiles" / "osm-standard.z0z4.mbtiles"


def _all_filepaths(dirpath: Path) -> list[str]:
return [str(f) for f in dirpath.rglob("*") if f.is_file()]


class TestRimraf:
def test_mbtiles2pyramid(self, tmp_path: Path, test_data_root: Path) -> None:
_mbtiles_filepath = _osm_standard_z0z4_mbtiles(test_data_root)
out_path = tmp_path / "osm-pyramid"
# copy mbtiles to tile pyramid
result = _run_cli(["cp", str(_mbtiles_filepath), str(out_path)])
assert result.returncode == 0
assert out_path.exists()
assert out_path.is_dir()
assert len(_all_filepaths(out_path)) > 0
all_paths = _all_filepaths(out_path)
png_paths = [p for p in all_paths if p.endswith(".png")]
assert len(png_paths) > 0
assert len(png_paths) == 341

# nuke the dir with rimrafer
result = _run_cli(["rimraf", str(out_path)])

assert result.returncode == 0

assert not out_path.exists()

0 comments on commit 8e9a503

Please sign in to comment.