Compare commits

...

4 Commits

Author SHA1 Message Date
0718ca1d53 refactor(ariadne): use monorepo Rust layout 2026-09-08 23:05:41 -04:00
2d1900e061 Merge remote-tracking branch 'origin/main' into feat/ariadne-cli-skeleton
# Conflicts:
#	src/ariadne/README.md
2026-09-08 23:05:14 -04:00
bf3a116797 refactor(ariadne): use io result in CLI skeleton 2026-09-08 20:01:39 -04:00
45ff66947a feat(ariadne): add CLI skeleton 2026-09-08 19:58:45 -04:00
7 changed files with 155 additions and 12 deletions

View File

@@ -3,13 +3,16 @@ load("@rules_rust//rust:defs.bzl", "rust_binary", "rust_library")
rust_library( rust_library(
name = "ariadne_lib", name = "ariadne_lib",
crate_name = "ariadne", crate_name = "ariadne",
srcs = ["src/lib.rs"], srcs = ["lib.rs"],
) )
rust_binary( rust_binary(
name = "ariadne", name = "ariadne",
crate_name = "ariadne_cli", crate_name = "ariadne_cli",
srcs = ["src/main.rs"], srcs = ["main.rs"],
deps = [":ariadne_lib"], deps = [
":ariadne_lib",
"//third_party/crates:clap",
],
) )

View File

@@ -3,6 +3,13 @@ name = "ariadne"
version = "0.1.0" version = "0.1.0"
edition = "2024" edition = "2024"
[lib]
path = "lib.rs"
[[bin]]
name = "ariadne"
path = "main.rs"
[dependencies] [dependencies]
clap = { version = "4", features = ["derive"] } clap = { version = "4", features = ["derive"] }
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }

View File

@@ -9,10 +9,15 @@ Clew owns extraction, preparation, and Ren'Py AST export.
Ariadne consumes Clew's `clew-renpy-ast-v1` JSON from `output/ast/`. Ariadne consumes Clew's `clew-renpy-ast-v1` JSON from `output/ast/`.
Ariadne maps that data into internal analysis state. Ariadne maps that data into internal analysis state.
## Bazel shape ## CLI skeleton
Run the current placeholder CLI with: Run all declared stages with:
```sh ```sh
bazel run //src/ariadne:ariadne bazel run //src/ariadne:ariadne -- --output-dir output/
``` ```
The skeleton currently declares one `parse` stage.
It writes a placeholder artifact under `output/graph/parse/`.
Use `--force` to remove stage output before rerunning the stage.

107
src/ariadne/lib.rs Normal file
View File

@@ -0,0 +1,107 @@
use std::collections::HashSet;
use std::fs;
use std::io::{self, Write};
use std::path::{Path, PathBuf};
pub type Result<T> = io::Result<T>;
#[derive(Debug, Clone)]
pub struct Config {
pub output_dir: PathBuf,
pub force: bool,
}
impl Config {
pub fn new(output_dir: PathBuf, force: bool) -> Self {
Self { output_dir, force }
}
}
#[derive(Debug, Clone, Copy, Eq, Hash, PartialEq)]
pub enum Stage {
Parse,
}
impl Stage {
pub const ALL: &'static [Stage] = &[Stage::Parse];
pub fn name(self) -> &'static str {
match self {
Stage::Parse => "parse",
}
}
pub fn prerequisites(self) -> &'static [Stage] {
match self {
Stage::Parse => &[],
}
}
}
pub fn run(config: &Config) -> Result<()> {
let stdout = io::stdout();
let mut handle = stdout.lock();
run_with_writer(config, &mut handle)
}
pub fn run_with_writer(config: &Config, writer: &mut dyn Write) -> Result<()> {
for stage in plan_stages(Stage::ALL) {
run_stage(stage, config, writer)?;
}
Ok(())
}
fn plan_stages(stages: &[Stage]) -> Vec<Stage> {
fn visit(stage: Stage, seen: &mut HashSet<Stage>, planned: &mut Vec<Stage>) {
if !seen.insert(stage) {
return;
}
for prerequisite in stage.prerequisites() {
visit(*prerequisite, seen, planned);
}
planned.push(stage);
}
let mut seen = HashSet::new();
let mut planned = Vec::new();
for stage in stages {
visit(*stage, &mut seen, &mut planned);
}
planned
}
fn run_stage(stage: Stage, config: &Config, writer: &mut dyn Write) -> Result<()> {
let stage_dir = stage_dir(&config.output_dir, stage);
if stage_dir.exists() {
if config.force {
fs::remove_dir_all(&stage_dir)?;
} else {
writeln!(
writer,
"SKIP {}: output exists at {}",
stage.name(),
stage_dir.display()
)?;
return Ok(());
}
}
writeln!(writer, "RUN {}", stage.name())?;
match stage {
Stage::Parse => run_parse(&stage_dir, writer)?,
}
Ok(())
}
fn stage_dir(output_dir: &Path, stage: Stage) -> PathBuf {
output_dir.join("graph").join(stage.name())
}
fn run_parse(stage_dir: &Path, writer: &mut dyn Write) -> Result<()> {
fs::create_dir_all(stage_dir)?;
let summary_path = stage_dir.join("summary.txt");
fs::write(&summary_path, "parse stage placeholder\n")?;
writeln!(writer, "WRITE {}", summary_path.display())?;
Ok(())
}

27
src/ariadne/main.rs Normal file
View File

@@ -0,0 +1,27 @@
use std::path::PathBuf;
use std::process::ExitCode;
use clap::Parser;
#[derive(Debug, Parser)]
#[command(name = "ariadne", about = "Analyze Clew output for a Ren'Py game.")]
struct Cli {
#[arg(long, default_value = "output")]
output_dir: PathBuf,
#[arg(long)]
force: bool,
}
fn main() -> ExitCode {
let cli = Cli::parse();
let config = ariadne::Config::new(cli.output_dir, cli.force);
match ariadne::run(&config) {
Ok(()) => ExitCode::SUCCESS,
Err(error) => {
eprintln!("ERROR: {error}");
ExitCode::FAILURE
}
}
}

View File

@@ -1,3 +0,0 @@
pub fn run() {
println!("Ariadne");
}

View File

@@ -1,3 +0,0 @@
fn main() {
ariadne::run();
}