use anyhow::Context as _;
use re_log_types::LogMsg;
use re_smart_channel::{Receiver, SmartChannelSource, SmartMessageSource};
use crate::FileContents;
#[derive(Clone)]
pub enum DataSource {
RrdHttpUrl(String),
#[cfg(not(target_arch = "wasm32"))]
FilePath(re_log_types::FileSource, std::path::PathBuf),
FileContents(re_log_types::FileSource, FileContents),
WebSocketAddr(String),
}
impl DataSource {
#[cfg(not(target_arch = "wasm32"))]
pub fn from_uri(file_source: re_log_types::FileSource, mut uri: String) -> DataSource {
use itertools::Itertools as _;
fn looks_like_windows_abs_path(path: &str) -> bool {
let path = path.as_bytes();
path.get(1).copied() == Some(b':') && path.get(2).copied() == Some(b'/')
}
fn looks_like_a_file_path(uri: &str) -> bool {
if uri.starts_with('/') {
return true; }
if looks_like_windows_abs_path(uri) {
return true;
}
let parts = uri.split('.').collect_vec();
if parts.len() <= 1 {
true } else if parts.len() == 2 {
is_known_file_extension(parts[1])
} else {
false }
}
let path = std::path::Path::new(&uri).to_path_buf();
if uri.starts_with("file://") || path.exists() {
DataSource::FilePath(file_source, path)
} else if uri.starts_with("http://")
|| uri.starts_with("https://")
|| (uri.starts_with("www.") && uri.ends_with(".rrd"))
{
DataSource::RrdHttpUrl(uri)
} else if uri.starts_with("ws://") || uri.starts_with("wss://") {
DataSource::WebSocketAddr(uri)
} else if looks_like_a_file_path(&uri) {
DataSource::FilePath(file_source, path)
} else if uri.ends_with(".rrd") {
DataSource::RrdHttpUrl(uri)
} else {
re_log::debug!("Assuming WebSocket endpoint");
if !uri.contains("://") {
uri = format!("{}://{uri}", re_ws_comms::PROTOCOL);
}
DataSource::WebSocketAddr(uri)
}
}
pub fn stream(
self,
on_msg: Option<Box<dyn Fn() + Send + Sync>>,
) -> anyhow::Result<Receiver<LogMsg>> {
re_tracing::profile_function!();
match self {
DataSource::RrdHttpUrl(url) => Ok(
re_log_encoding::stream_rrd_from_http::stream_rrd_from_http_to_channel(url, on_msg),
),
#[cfg(not(target_arch = "wasm32"))]
DataSource::FilePath(file_source, path) => {
let (tx, rx) = re_smart_channel::smart_channel(
SmartMessageSource::File(path.clone()),
SmartChannelSource::File(path.clone()),
);
let store_id = re_log_types::StoreId::random(re_log_types::StoreKind::Recording);
crate::load_file_path::load_file_path(store_id, file_source, path.clone(), tx)
.with_context(|| format!("{path:?}"))?;
if let Some(on_msg) = on_msg {
on_msg();
}
Ok(rx)
}
DataSource::FileContents(file_source, file_contents) => {
let name = file_contents.name.clone();
let (tx, rx) = re_smart_channel::smart_channel(
SmartMessageSource::File(name.clone().into()),
SmartChannelSource::File(name.clone().into()),
);
let store_id = re_log_types::StoreId::random(re_log_types::StoreKind::Recording);
crate::load_file_contents::load_file_contents(
store_id,
file_source,
file_contents,
tx,
)
.with_context(|| format!("{name:?}"))?;
if let Some(on_msg) = on_msg {
on_msg();
}
Ok(rx)
}
DataSource::WebSocketAddr(rerun_server_ws_url) => {
crate::web_sockets::connect_to_ws_url(&rerun_server_ws_url, on_msg)
}
}
}
}
#[cfg(not(target_arch = "wasm32"))]
fn is_known_file_extension(extension: &str) -> bool {
extension == "rrd"
|| crate::SUPPORTED_MESH_EXTENSIONS.contains(&extension)
|| crate::SUPPORTED_IMAGE_EXTENSIONS.contains(&extension)
}
#[cfg(not(target_arch = "wasm32"))]
#[test]
fn test_data_source_from_uri() {
use re_log_types::FileSource;
let file = [
"file://foo",
"foo.rrd",
"foo.png",
"/foo/bar/baz",
"D:/file",
];
let http = [
"http://foo.zip",
"https://foo.zip",
"example.zip/foo.rrd",
"www.foo.zip/foo.rrd",
];
let ws = ["ws://foo.zip", "wss://foo.zip", "127.0.0.1"];
let file_source = FileSource::DragAndDrop;
for uri in file {
assert!(
matches!(
DataSource::from_uri(file_source, uri.to_owned()),
DataSource::FilePath { .. }
),
"Expected {uri:?} to be categorized as FilePath"
);
}
for uri in http {
assert!(
matches!(
DataSource::from_uri(file_source, uri.to_owned()),
DataSource::RrdHttpUrl(_)
),
"Expected {uri:?} to be categorized as RrdHttpUrl"
);
}
for uri in ws {
assert!(
matches!(
DataSource::from_uri(file_source, uri.to_owned()),
DataSource::WebSocketAddr(_)
),
"Expected {uri:?} to be categorized as WebSocketAddr"
);
}
}