feat(import): identity import API + FIFA17 real-profile dry-run importer
openfut-identity: - insert_existing_mapping(game,kind,core_id,external_id): preserve an existing external wire id instead of minting; idempotent for an identical mapping, rejects conflicting forward/reverse with IdError::Conflict, persists atomically. - persisted per-scope allocator watermark (set_watermark/watermark_for) so a future mint continues past the source high-water even across burned-id gaps; next id = max(base_floor, live_max+1, watermark). Backward-compatible on-disk format (legacy bare [Row] still loads). +4 tests (10 total). openfut-import-fifa17 (new): read-only dry-run analysis of a real FIFA17 Python profile for a faithful Core import. Enforces disjoint item-class balance; proposes profile-derived CardDefinitions keyed fifa17_<resourceId> (base vs versioned never collapse) with a resourceId-group consistency gate (hard-fail on disagreement, never pick a winner) and honest buildability (roster name + version formula + metadata, never fabricated); plans owned-instance identity (preserve Python wire ids, preserve nextItemId watermark); checks active-squad coverage. --apply/--emit-content refuse to write in this phase. 11 tests. Real profile (33068179/CAGE) dry-run: 1982 items balance (1962 players + 17 consumables + 3 staff); 1681 supported defs (155 base + 1535 versioned), 9 NoName unsupported, 1 hard conflict (resourceId 169193: one of 4 copies has a divergent nation/team/league); 1949 importable player instances, watermark 100004617 -> first new alloc 100004617; active squad f433 fully supported. fmt + clippy -D warnings clean.
This commit is contained in:
@@ -0,0 +1,70 @@
|
||||
//! `openfut-import-fifa17` — real FIFA 17 profile import.
|
||||
//!
|
||||
//! This phase implements the **read-only dry-run** only. `--apply` and
|
||||
//! `--emit-content` are recognised but deliberately refuse to run (and exit
|
||||
//! nonzero) so nothing is written before the apply/emission phase lands.
|
||||
|
||||
use anyhow::{bail, Context, Result};
|
||||
use std::process::ExitCode;
|
||||
|
||||
fn main() -> ExitCode {
|
||||
match run() {
|
||||
Ok(code) => code,
|
||||
Err(e) => {
|
||||
eprintln!("import-fifa17: {e:#}");
|
||||
ExitCode::FAILURE
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn print_help() {
|
||||
eprintln!(
|
||||
"openfut-import-fifa17 --profile <fifa17_profile.json> [--roster <roster.json>]\n\
|
||||
\n\
|
||||
Read-only dry-run: analyses a real FIFA 17 Python profile for a faithful\n\
|
||||
OpenFUT Core import and reports item accounting, observed definitions,\n\
|
||||
identity preservation, and squad coverage. Exits nonzero on unsafe blockers.\n\
|
||||
\n\
|
||||
--profile <path> (required) source fifa17_profile.json\n\
|
||||
--roster <path> roster.json for player names (default fifa17-recon/data/roster.json)\n\
|
||||
--apply REFUSED in this phase (no writes)\n\
|
||||
--emit-content <dir> REFUSED in this phase (no generated content)\n"
|
||||
);
|
||||
}
|
||||
|
||||
fn run() -> Result<ExitCode> {
|
||||
let mut profile_path: Option<String> = None;
|
||||
let mut roster_path = "fifa17-recon/data/roster.json".to_string();
|
||||
let mut args = std::env::args().skip(1);
|
||||
while let Some(a) = args.next() {
|
||||
match a.as_str() {
|
||||
"--profile" => profile_path = Some(args.next().context("--profile needs a path")?),
|
||||
"--roster" => roster_path = args.next().context("--roster needs a path")?,
|
||||
"--dry-run" => {}
|
||||
"--apply" => bail!(
|
||||
"--apply is not implemented in this phase (dry-run + identity import API only); refusing to write the Core DB or identity store"
|
||||
),
|
||||
"--emit-content" => bail!(
|
||||
"--emit-content is not implemented in this phase; refusing to write generated content"
|
||||
),
|
||||
"-h" | "--help" => {
|
||||
print_help();
|
||||
return Ok(ExitCode::SUCCESS);
|
||||
}
|
||||
other => bail!("unknown argument: {other} (try --help)"),
|
||||
}
|
||||
}
|
||||
let profile_path = profile_path.context("--profile <path> is required (try --help)")?;
|
||||
|
||||
let profile = openfut_import_fifa17::load_profile(&profile_path)?;
|
||||
let roster = openfut_import_fifa17::load_roster(&roster_path)?;
|
||||
let report = openfut_import_fifa17::analyze(&profile, &roster);
|
||||
print!("{report}");
|
||||
|
||||
if report.has_blockers() {
|
||||
eprintln!("import-fifa17: DRY-RUN FAILED — unsafe blockers present (see above)");
|
||||
Ok(ExitCode::FAILURE)
|
||||
} else {
|
||||
Ok(ExitCode::SUCCESS)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user