parse-mediawiki-sql 0.4.0

A parser for SQL dumps of a MediaWiki database
Documentation
use memmap::Mmap;
use parse_mediawiki_sql::{FromSqlTuple, iterate_sql_insertions};
use std::{fs::File, path::{PathBuf, Path}};
use joinery::Joinable;

unsafe fn memory_map(path: &Path) -> Mmap {
    Mmap::map(
        &File::open(path)
            .unwrap_or_else(|e| panic!("Failed to open {}: {}", &path.display(), e)),
    )
    .unwrap_or_else(|e| panic!("Failed to memory-map {}: {}", &path.display(), e))
}

fn print_row_count<'a, T: FromSqlTuple<'a> + 'a>(sql_script: &'a [u8]) {
    let mut iter = iterate_sql_insertions::<'a, T>(sql_script);
    let count = iter.count();
    match iter.finish() {
        Ok(_) => {
            println!("{} rows parsed", count);
        }
        Err(e) => {
            match e {
                nom::Err::Incomplete(_) => {
                    eprintln!("Needed more data");
                }
                nom::Err::Error(e) | nom::Err::Failure(e) => {
                    eprintln!("{}", e);
                }
            }
        }
    }
}

macro_rules! do_with_table {
    (
        $function:ident::<
            match $table_name:ident {
                $(
                $table:ident => $type:ident
                ),+
                $(,)?
            }
        >(&$script:ident)
        
    ) => {
        use parse_mediawiki_sql::schemas::*;
        match $table_name {
            $(
                stringify!($table) => $function::<$type>(&$script),
            )+
            _ => {
                eprintln!(
                    "Expected table name “{}” to be one of {}",
                    $table_name,
                    [
                        $(
                            stringify!($table),
                        )+
                    ].join_with(", "),
                );
                std::process::exit(1);
            }
        }

    }
}

fn main() {
    let mut args = std::env::args_os().skip(1);
    let args = (args.next().map(PathBuf::from), args.next());
    let (sql_path, table) = match &args {
        (Some(sql_path), Some(table)) => {
            if let Some(table) = table.to_str() {
                (sql_path, table)

            } else {
                eprintln!("expected SQL table name (second argument) to be valid UTF-8");
                std::process::exit(1);
            }
        },
        (Some(sql_path), None) => {
            if let Some(table) = sql_path.file_stem().and_then(|s| s.to_str()) {
                (sql_path, table)
            } else {
                eprintln!("no SQL table name (second argument); expected file stem that is valid UTF-8");
                std::process::exit(1);
            }
        },
        (None, None) => {
            eprintln!("expected path to SQL script as first argument");
            std::process::exit(1);
        },
        _ => unreachable!("test"),
    };

    let script = unsafe { memory_map(&sql_path) };

    do_with_table! {
        print_row_count::<
            match table {
                category => Category,
                categorylinks => CategoryLinks,
                change_tag_def => ChangeTagDef,
                change_tag => ChangeTag,
                externallinks => ExternalLinks,
                image => Image,
                imagelinks => ImageLinks,
                iwlinks => InterwikiLinks,
                langlinks => LangLinks,
                page_restrictions => PageRestrictions,
                page => Page,
                pagelinks => PageLinks,
                page_props => PageProps,
                protected_titles => ProtectedTitles,
                redirect => Redirect,
                sites => Site,
                site_stats => SiteStats,
                templatelinks => TemplateLinks,
                user_former_groups => UserFormerGroups,
                user_groups => UserGroups,
                wbc_entity_usage => WikibaseClientEntityUsage,
            }
        >(&script)
    }
}