mod bear;
mod markdown;
mod notion;
mod obsidian;
use bear::BearZipProvider;
use markdown::MarkdownZipProvider;
use notion::{NotionHtmlZipProvider, NotionMarkdownZipProvider, NotionZipProvider};
use obsidian::ObsidianProvider;
pub(in crate::planner::providers) use super::folder_hierarchy_deltas;
use super::{
ImportAssetRef, ImportDocRef, IndexedArchive, add_asset_batch, doc_snapshot, empty_batch, entry_from_indexed,
extract_html_icon, extract_html_title, file_name, folders_for_doc_path, frontmatter_meta, has_emitted_assets,
hash_bytes, html_to_markdown, mime_from_path, normalize_import_path, parse_date_millis, parse_link_span,
push_skipped_doc_warning, read_archive, register_csv_path, register_page_path, resolve_path, strip_extension,
strip_frontmatter,
};
use crate::ImportRegistry;
pub(crate) fn register_builtin_providers(registry: &mut ImportRegistry) {
registry.register(MarkdownZipProvider);
registry.register(NotionZipProvider);
registry.register(NotionMarkdownZipProvider);
registry.register(NotionHtmlZipProvider);
registry.register(ObsidianProvider);
registry.register(BearZipProvider);
}
#[cfg(test)]
pub(in crate::planner::providers) mod tests {
pub(in crate::planner::providers) fn zip(entries: &[(&str, &[u8])]) -> Vec<u8> {
use std::io::{Cursor, Write};
use zip::{ZipWriter, write::SimpleFileOptions};
let mut cursor = Cursor::new(Vec::new());
{
let mut writer = ZipWriter::new(&mut cursor);
for (path, bytes) in entries {
writer.start_file(path, SimpleFileOptions::default()).unwrap();
writer.write_all(bytes).unwrap();
}
writer.finish().unwrap();
}
cursor.into_inner()
}
pub(in crate::planner::providers) fn temp_dir() -> std::path::PathBuf {
let path = std::env::temp_dir().join(format!("affine-importer-dir-{}", nanoid::nanoid!()));
std::fs::create_dir_all(&path).unwrap();
path
}
fn zip_file(bytes: impl AsRef<[u8]>) -> std::path::PathBuf {
use std::io::Write;
let path = std::env::temp_dir().join(format!("affine-importer-{}.zip", nanoid::nanoid!()));
let mut file = std::fs::OpenOptions::new()
.write(true)
.create_new(true)
.open(&path)
.unwrap();
file.write_all(bytes.as_ref()).unwrap();
path
}
pub(in crate::planner::providers) fn write_file(root: &std::path::Path, path: &str, bytes: &[u8]) {
let path = root.join(path);
std::fs::create_dir_all(path.parent().unwrap()).unwrap();
std::fs::write(path, bytes).unwrap();
}
pub(in crate::planner::providers) fn unlimited_limits() -> crate::ImportBatchLimits {
crate::ImportBatchLimits {
max_docs: usize::MAX,
max_blobs: usize::MAX,
max_blob_bytes: u64::MAX,
}
}
pub(in crate::planner::providers) fn merge_batch(target: &mut crate::ImportBatch, batch: crate::ImportBatch) {
target.docs.extend(batch.docs);
target.blobs.extend(batch.blobs);
target.folders.extend(batch.folders);
target.tags.extend(batch.tags);
target.icons.extend(batch.icons);
target.warnings.extend(batch.warnings);
target.progress = batch.progress;
target.entry_id = target.entry_id.take().or(batch.entry_id);
target.is_workspace_file |= batch.is_workspace_file;
}
pub(in crate::planner::providers) fn merge_cursor(
mut cursor: Box<dyn crate::ImportCursor>,
) -> crate::ImportResult<crate::ImportBatch> {
let mut merged = super::empty_batch(0);
while let Some(batch) = cursor.next_batch()? {
merge_batch(&mut merged, batch);
}
merged.done = true;
Ok(merged)
}
pub(in crate::planner::providers) fn import_cursor(
format: &str,
source: crate::ImportSource,
options: crate::ImportOptions,
limits: crate::ImportBatchLimits,
) -> crate::ImportResult<Box<dyn crate::ImportCursor>> {
crate::ImportRegistry::with_builtin().create_cursor(format, source, options, limits)
}
pub(in crate::planner::providers) fn zip_cursor(
format: &str,
bytes: impl AsRef<[u8]>,
options: crate::ImportOptions,
limits: crate::ImportBatchLimits,
) -> crate::ImportResult<Box<dyn crate::ImportCursor>> {
import_cursor(format, crate::ImportSource::FilePath(zip_file(bytes)), options, limits)
}
pub(in crate::planner::providers) fn plan_zip(
format: &str,
bytes: impl AsRef<[u8]>,
) -> crate::ImportResult<crate::ImportBatch> {
zip_cursor(format, bytes, crate::ImportOptions::default(), unlimited_limits()).and_then(merge_cursor)
}
}