Skip to content

Commit 5931809

Browse files
authored
DataLoaders 4: add generic folder DataLoader (#4520)
Load folders filled with whatever. Go nuts. The loader has no particular semantics (e.g. HIVE partitioning): it just loads everything in a folder using the appropriate loader(s) for each file. Checks: - [x] `cargo r -p rerun-cli --no-default-features --features native_viewer -- examples/assets` - [x] Native: `File > Open > examples/assets/` - [x] Native: `Drag-n-drop > examples/assets/` --- Part of a series of PRs to make it possible to load _any_ file from the local filesystem, by any means, on web and native: - #4516 - #4517 - #4518 - #4519 - #4520 - #4521 - TODO: register custom loaders - TODO: high level docs and guides for everything related to loading files
1 parent 82f17dc commit 5931809

File tree

3 files changed

+86
-4
lines changed

3 files changed

+86
-4
lines changed
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
/// Recursively oads entire directories, using the appropriate [`crate::DataLoader`]:s for each
2+
/// files within.
3+
//
4+
// TODO(cmc): There are a lot more things than can be done be done when it comes to the semantics
5+
// of a folder, e.g.: HIVE-like partitioning, similarly named files with different indices and/or
6+
// timestamps (e.g. a folder of video frames), etc.
7+
// We could support some of those at some point, or at least add examples to show users how.
8+
pub struct DirectoryLoader;
9+
10+
impl crate::DataLoader for DirectoryLoader {
11+
#[inline]
12+
fn name(&self) -> String {
13+
"rerun.data_loaders.Directory".into()
14+
}
15+
16+
#[cfg(not(target_arch = "wasm32"))]
17+
fn load_from_path(
18+
&self,
19+
store_id: re_log_types::StoreId,
20+
dirpath: std::path::PathBuf,
21+
tx: std::sync::mpsc::Sender<crate::LoadedData>,
22+
) -> Result<(), crate::DataLoaderError> {
23+
if dirpath.is_file() {
24+
return Ok(()); // simply not interested
25+
}
26+
27+
re_tracing::profile_function!(dirpath.display().to_string());
28+
29+
re_log::debug!(?dirpath, loader = self.name(), "Loading directory…",);
30+
31+
for entry in walkdir::WalkDir::new(&dirpath) {
32+
let entry = match entry {
33+
Ok(entry) => entry,
34+
Err(err) => {
35+
re_log::error!(loader = self.name(), ?dirpath, %err, "Failed to open filesystem entry");
36+
continue;
37+
}
38+
};
39+
40+
let filepath = entry.path();
41+
if filepath.is_file() {
42+
let store_id = store_id.clone();
43+
let filepath = filepath.to_owned();
44+
let tx = tx.clone();
45+
46+
// NOTE: spawn is fine, this whole function is native-only.
47+
rayon::spawn(move || {
48+
let data = match crate::load_file::load(&store_id, &filepath, false, None) {
49+
Ok(data) => data,
50+
Err(err) => {
51+
re_log::error!(?filepath, %err, "Failed to load directory entry");
52+
return;
53+
}
54+
};
55+
56+
for datum in data {
57+
if tx.send(datum).is_err() {
58+
break;
59+
}
60+
}
61+
});
62+
}
63+
}
64+
65+
Ok(())
66+
}
67+
68+
#[inline]
69+
fn load_from_file_contents(
70+
&self,
71+
_store_id: re_log_types::StoreId,
72+
_path: std::path::PathBuf,
73+
_contents: std::borrow::Cow<'_, [u8]>,
74+
_tx: std::sync::mpsc::Sender<crate::LoadedData>,
75+
) -> Result<(), crate::DataLoaderError> {
76+
// TODO(cmc): This could make sense to implement for e.g. archive formats (zip, tar, …)
77+
Ok(()) // simply not interested
78+
}
79+
}

crates/re_data_source/src/data_loader/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,7 @@ static BUILTIN_LOADERS: Lazy<Vec<Arc<dyn DataLoader>>> = Lazy::new(|| {
205205
vec![
206206
Arc::new(RrdLoader) as Arc<dyn DataLoader>,
207207
Arc::new(ArchetypeLoader),
208+
Arc::new(DirectoryLoader),
208209
]
209210
});
210211

@@ -217,7 +218,9 @@ pub fn iter_loaders() -> impl ExactSizeIterator<Item = Arc<dyn DataLoader>> {
217218
// ---
218219

219220
mod loader_archetype;
221+
mod loader_directory;
220222
mod loader_rrd;
221223

222224
pub use self::loader_archetype::ArchetypeLoader;
225+
pub use self::loader_directory::DirectoryLoader;
223226
pub use self::loader_rrd::RrdLoader;

crates/re_data_source/src/load_file.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -98,11 +98,11 @@ pub fn extension(path: &std::path::Path) -> String {
9898
/// This does _not_ access the filesystem.
9999
#[inline]
100100
pub fn is_associated_with_builtin_loader(path: &std::path::Path, is_dir: bool) -> bool {
101-
!is_dir && crate::is_supported_file_extension(&extension(path))
101+
is_dir || crate::is_supported_file_extension(&extension(path))
102102
}
103103

104104
/// Prepares an adequate [`re_log_types::StoreInfo`] [`LogMsg`] given the input.
105-
fn prepare_store_info(
105+
pub(crate) fn prepare_store_info(
106106
store_id: &re_log_types::StoreId,
107107
file_source: FileSource,
108108
path: &std::path::Path,
@@ -139,7 +139,7 @@ fn prepare_store_info(
139139
/// - On native, this is filled asynchronously from other threads.
140140
/// - On wasm, this is pre-filled synchronously.
141141
#[cfg_attr(target_arch = "wasm32", allow(clippy::needless_pass_by_value))]
142-
fn load(
142+
pub(crate) fn load(
143143
store_id: &re_log_types::StoreId,
144144
path: &std::path::Path,
145145
is_dir: bool,
@@ -218,7 +218,7 @@ fn load(
218218
/// Forwards the data in `rx_loader` to `tx`, taking care of necessary conversions, if any.
219219
///
220220
/// Runs asynchronously from another thread on native, synchronously on wasm.
221-
fn send(
221+
pub(crate) fn send(
222222
store_id: &re_log_types::StoreId,
223223
rx_loader: std::sync::mpsc::Receiver<LoadedData>,
224224
tx: &Sender<LogMsg>,

0 commit comments

Comments
 (0)