#[cfg(not(feature = "duckdb"))]
use std::path::{Path, PathBuf};
use floe_core::config::RootConfig;
#[cfg(not(feature = "duckdb"))]
use floe_core::ConfigError;
use floe_core::FloeResult;
#[cfg(not(feature = "duckdb"))]
const COMPANION_STEM: &str = "floe-duckdb";
#[cfg_attr(feature = "duckdb", allow(dead_code))]
pub fn config_targets_duckdb(config: &RootConfig) -> bool {
config
.entities
.iter()
.any(|entity| entity.sink.accepted.format == "duckdb")
}
#[cfg(feature = "duckdb")]
pub fn maybe_delegate_duckdb(_config: &RootConfig) -> FloeResult<()> {
Ok(())
}
#[cfg(not(feature = "duckdb"))]
pub fn maybe_delegate_duckdb(config: &RootConfig) -> FloeResult<()> {
if !config_targets_duckdb(config) {
return Ok(());
}
let companion = find_companion().ok_or_else(|| {
Box::new(ConfigError(format!(
"this config writes to a DuckDB sink, but this is the lean `floe` build \
without DuckDB support and no `{COMPANION_STEM}` companion was found on \
PATH or alongside this executable. Install the DuckDB build via the \
`ghcr.io/malon64/floe-duckdb` image, the `floe-duckdb` release binary, \
or `cargo install floe-cli --features duckdb`."
))) as Box<dyn std::error::Error + Send + Sync>
})?;
let status = std::process::Command::new(&companion)
.args(std::env::args_os().skip(1))
.status()
.map_err(|err| {
Box::new(ConfigError(format!(
"failed to launch DuckDB companion at {}: {err}",
companion.display()
))) as Box<dyn std::error::Error + Send + Sync>
})?;
std::process::exit(status.code().unwrap_or(1));
}
#[cfg(not(feature = "duckdb"))]
fn find_companion() -> Option<PathBuf> {
let name = format!("{COMPANION_STEM}{}", std::env::consts::EXE_SUFFIX);
if let Some(dir) = std::env::current_exe()
.ok()
.and_then(|exe| exe.canonicalize().ok())
.and_then(|exe| exe.parent().map(Path::to_path_buf))
{
let candidate = dir.join(&name);
if is_executable_file(&candidate) {
return Some(candidate);
}
}
if let Some(path) = std::env::var_os("PATH") {
for dir in std::env::split_paths(&path) {
if !dir.is_absolute() {
continue;
}
let candidate = dir.join(&name);
if is_executable_file(&candidate) {
return Some(candidate);
}
}
}
None
}
#[cfg(not(feature = "duckdb"))]
fn is_executable_file(path: &Path) -> bool {
if !path.is_file() {
return false;
}
#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt;
std::fs::metadata(path)
.map(|meta| meta.permissions().mode() & 0o111 != 0)
.unwrap_or(false)
}
#[cfg(not(unix))]
{
true
}
}