mod args;
pub use args::Args;
use anyhow::Context;
use clap::Parser;
use surreal_sync_core::{Schema, SurrealSink};
use surreal_sync_runtime::ApplyOpts;
use surreal_sync_runtime::{init, load_transforms_from_args, merge_inplace_boxed, SinkConnect};
use super::{sync_with_transforms, Config};
pub use surreal_sync_core::Value;
pub use surreal_sync_runtime::SurrealConfig;
pub use surreal_sync_runtime::{FlattenId, InPlaceTransform, Pipeline};
fn build_config(args: &Args) -> anyhow::Result<Config> {
let schema = if let Some(schema_path) = &args.schema_file {
Some(
Schema::from_file(schema_path)
.with_context(|| format!("Failed to load sync schema from {schema_path:?}"))?
.to_database_schema(),
)
} else {
None
};
Ok(Config {
sources: vec![],
files: vec![args.path.clone().into()],
s3_uris: vec![],
http_uris: vec![],
id_field: args.id_field.clone(),
id_columns: args.id_columns.clone(),
conversion_rules: args.conversion_rules.clone(),
batch_size: args.surreal.batch_size,
dry_run: args.surreal.dry_run,
schema,
})
}
fn surreal_config_from_args(args: &Args) -> SurrealConfig {
args.surreal
.to_config(args.to_namespace.clone(), args.to_database.clone())
}
pub async fn run_sync<S: SurrealSink>(
args: &Args,
sink: &S,
pipeline: &Pipeline,
apply_opts: &ApplyOpts,
) -> anyhow::Result<()> {
tracing::info!("Starting JSONL import");
tracing::info!("Target: {}/{}", args.to_namespace, args.to_database);
if args.surreal.dry_run {
tracing::info!("Running in dry-run mode - no data will be written");
}
let config = build_config(args)?;
sync_with_transforms(sink, config, pipeline, apply_opts).await?;
tracing::info!("JSONL import completed successfully");
Ok(())
}
pub async fn run_with_extra_transforms<S: SinkConnect>(
args: Args,
extra: impl IntoIterator<Item = Box<dyn InPlaceTransform>>,
) -> anyhow::Result<()> {
let (pipeline, apply_opts) = merge_inplace_boxed(args.transforms_config.as_deref(), extra)?;
let config = surreal_config_from_args(&args);
let sink = S::connect(&config).await?;
run_sync(&args, &sink, &pipeline, &apply_opts).await
}
pub async fn run_args_with_sink<S: SurrealSink>(args: Args, sink: &S) -> anyhow::Result<()> {
let (pipeline, apply_opts) = load_transforms_from_args(args.transforms_config.as_deref())?;
run_sync(&args, sink, &pipeline, &apply_opts).await
}
#[derive(Parser)]
#[command(
name = "surreal-sync-jsonl",
about = "Import from JSONL into SurrealDB (same flags as `surreal-sync from jsonl`)"
)]
struct EmbedCli {
#[command(flatten)]
args: Args,
}
pub async fn run<S: SinkConnect>(
extra: impl IntoIterator<Item = Box<dyn InPlaceTransform>>,
) -> anyhow::Result<()> {
init();
let cli = EmbedCli::parse();
run_with_extra_transforms::<S>(cli.args, extra).await
}