mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-10-27 11:17:01 -06:00
Asset policy: no Adobe iconography/images; per-asset attribution in ASSETS.md enforced by xtask assets (in ci). Offset Path: fix area loss for offsets larger than curvature radius
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
049c2baebd
commit
94e72f490e
@@ -0,0 +1,81 @@
|
||||
//! `cargo xtask assets`: every non-code asset must be attributed in `ASSETS.md`.
|
||||
//!
|
||||
//! Scans all git-tracked (and untracked, not ignored) files with an asset extension, plus
|
||||
//! everything under `assets/`, `docs/images/` and `examples/`, and fails if a path is not listed
|
||||
//! as `` `path` `` in ASSETS.md. See the asset policy in AGENTS.md.
|
||||
|
||||
use std::path::Path;
|
||||
use std::process::Command;
|
||||
|
||||
const ASSET_EXT: &[&str] = &[
|
||||
"png",
|
||||
"jpg",
|
||||
"jpeg",
|
||||
"gif",
|
||||
"webp",
|
||||
"bmp",
|
||||
"tif",
|
||||
"tiff",
|
||||
"ico",
|
||||
"icns",
|
||||
"svg",
|
||||
"pdf",
|
||||
"ai",
|
||||
"eps",
|
||||
"psd",
|
||||
"icc",
|
||||
"icm",
|
||||
"ttf",
|
||||
"otf",
|
||||
"woff",
|
||||
"woff2",
|
||||
"ase",
|
||||
"aco",
|
||||
"abr",
|
||||
"drawcraft",
|
||||
"mp4",
|
||||
"wav",
|
||||
"mp3",
|
||||
];
|
||||
const ASSET_DIRS: &[&str] = &["assets/", "docs/images/", "examples/"];
|
||||
|
||||
pub fn is_asset(path: &str) -> bool {
|
||||
let ext = Path::new(path).extension().and_then(|e| e.to_str()).map(str::to_ascii_lowercase).unwrap_or_default();
|
||||
ASSET_DIRS.iter().any(|d| path.starts_with(d)) || ASSET_EXT.contains(&ext.as_str())
|
||||
}
|
||||
|
||||
/// Paths that need an ASSETS.md entry but lack one.
|
||||
pub fn missing(files: &[String], assets_md: &str) -> Vec<String> {
|
||||
files.iter().filter(|f| is_asset(f) && !assets_md.contains(&format!("`{f}`"))).cloned().collect()
|
||||
}
|
||||
|
||||
pub fn run(root: &Path) -> Result<(), String> {
|
||||
let out = Command::new("git")
|
||||
.current_dir(root)
|
||||
.args(["ls-files", "--cached", "--others", "--exclude-standard"])
|
||||
.output()
|
||||
.map_err(|e| format!("git ls-files: {e}"))?;
|
||||
let files: Vec<String> = String::from_utf8_lossy(&out.stdout).lines().filter(|l| root.join(l).exists()).map(str::to_owned).collect();
|
||||
let md = std::fs::read_to_string(root.join("ASSETS.md")).map_err(|e| format!("ASSETS.md: {e}"))?;
|
||||
let miss = missing(&files, &md);
|
||||
if miss.is_empty() {
|
||||
println!("assets: all {} asset files attributed in ASSETS.md", files.iter().filter(|f| is_asset(f)).count());
|
||||
Ok(())
|
||||
} else {
|
||||
Err(format!("{} asset file(s) lack an ASSETS.md entry (author, source, licence):\n {}", miss.len(), miss.join("\n ")))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn detects_unattributed_assets() {
|
||||
let files = vec!["assets/icons/a.svg".to_string(), "crates/x/src/lib.rs".into(), "docs/images/b.png".into(), "tests/fixture.png".into()];
|
||||
let md = "| `assets/icons/a.svg` | me | here | MIT |";
|
||||
assert_eq!(missing(&files, md), vec!["docs/images/b.png".to_string(), "tests/fixture.png".into()]);
|
||||
assert!(!is_asset("crates/x/src/lib.rs"));
|
||||
assert!(is_asset("assets/fonts/OFL.txt"));
|
||||
}
|
||||
}
|
||||
+5
-1
@@ -3,6 +3,7 @@
|
||||
//! Pure Rust (std + serde_json). External tools (`cargo`, `curl`, `tar`) are
|
||||
//! invoked through `std::process::Command`.
|
||||
|
||||
mod assets;
|
||||
mod bundle;
|
||||
mod layers;
|
||||
mod stats;
|
||||
@@ -14,9 +15,10 @@ const USAGE: &str = "\
|
||||
usage: cargo xtask <command>
|
||||
|
||||
commands:
|
||||
assets check that every icon/image/font/asset is attributed in ASSETS.md
|
||||
layers enforce the crate dependency layering (plan/architecture.md §3)
|
||||
wasm cargo check --target wasm32-unknown-unknown for the wasm-safe crates
|
||||
ci fmt --check, clippy -D warnings, test, layers, wasm (stops at first failure)
|
||||
ci fmt --check, clippy -D warnings, test, assets, layers, wasm (stops at first failure)
|
||||
corpus [--download]
|
||||
show where test corpora live; --download fetches PngSuite into corpus/pngsuite
|
||||
bundle build dist/DrawCraft.app (macOS) with an icon rendered by DrawCraft
|
||||
@@ -27,6 +29,7 @@ fn main() -> ExitCode {
|
||||
let args: Vec<String> = std::env::args().skip(1).collect();
|
||||
let rest: Vec<&str> = args.iter().skip(1).map(String::as_str).collect();
|
||||
let result = match args.first().map(String::as_str) {
|
||||
Some("assets") => assets::run(&root()),
|
||||
Some("layers") => cmd_layers(),
|
||||
Some("wasm") => cmd_wasm(),
|
||||
Some("ci") => cmd_ci(),
|
||||
@@ -166,6 +169,7 @@ fn cmd_ci() -> Result<(), String> {
|
||||
run(c, "cargo test --workspace")
|
||||
}),
|
||||
),
|
||||
("assets", Box::new(|| assets::run(&root()))),
|
||||
("layers", Box::new(cmd_layers)),
|
||||
("wasm", Box::new(cmd_wasm)),
|
||||
];
|
||||
|
||||
Reference in New Issue
Block a user