use catwalk3d::{formats, saved_preview};
use serde_json::{Value, json};
use std::collections::BTreeSet;
fn extensions(registries: &[&[&str]]) -> Vec<String> {
registries
.iter()
.flat_map(|registry| registry.iter())
.map(|extension| extension.trim_start_matches('.').to_ascii_lowercase())
.collect::<BTreeSet<_>>()
.into_iter()
.collect()
}
pub(crate) fn json() -> Value {
let registered = extensions(&[
formats::MODEL_EXTENSIONS,
formats::IMAGE_EXTENSIONS,
saved_preview::EXTENSIONS,
formats::NATIVE_GEOMETRY_EXTENSIONS,
]);
let entries: Vec<_> = registered
.iter()
.map(|extension| {
let description = description(extension).map_or_else(
|| format!("{} file", extension.to_ascii_uppercase()),
str::to_owned,
);
json!({"extension": extension, "description": description})
})
.collect();
json!({
"schema_version": 1,
"catwalk_version": env!("CARGO_PKG_VERSION"),
"extensions": registered,
"formats": entries,
"categories": {
"geometry": extensions(&[
formats::MODEL_EXTENSIONS,
formats::NATIVE_GEOMETRY_EXTENSIONS,
]),
"image": extensions(&[formats::IMAGE_EXTENSIONS]),
"saved_preview": extensions(&[saved_preview::EXTENSIONS]),
"native_geometry": extensions(&[formats::NATIVE_GEOMETRY_EXTENSIONS]),
},
})
}
fn description(extension: &str) -> Option<&'static str> {
Some(match extension {
"stl" => "STL mesh",
"obj" => "Wavefront OBJ model",
"ply" => "PLY polygon or point cloud",
"off" => "Object File Format mesh",
"pts" => "PTS point cloud",
"ptx" => "PTX point cloud",
"pcd" => "PCD point cloud",
"splat" => "Gaussian splat point cloud",
"gltf" => "glTF model",
"glb" => "Binary glTF model",
"3mf" => "3D Manufacturing Format model",
"vtk" => "VTK legacy dataset",
"vtp" => "VTK PolyData XML dataset",
"vtu" => "VTK UnstructuredGrid XML dataset",
"vtr" => "VTK RectilinearGrid XML dataset",
"vts" => "VTK StructuredGrid XML dataset",
"vtm" => "VTK MultiBlock XML dataset",
"vti" => "VTK ImageData XML volume",
"dae" => "COLLADA model",
"las" => "LAS point cloud",
"laz" => "Compressed LAS point cloud",
"step" | "stp" | "stpnc" | "p21" | "210" => "STEP model",
"mdl" => "Quake MDL model",
"3ds" => "3D Studio model",
"wrl" | "vrml" => "VRML model",
"gml" => "CityGML model",
"usd" => "Universal Scene Description stage",
"usda" => "Universal Scene Description stage (text)",
"usdc" => "Universal Scene Description stage (binary)",
"usdz" => "Universal Scene Description archive",
"spz" => "Compressed SPZ Gaussian splat point cloud",
"nrrd" | "nhdr" => "NRRD scalar volume",
"mhd" | "mha" => "MetaImage scalar volume",
"dxf" => "AutoCAD DXF drawing",
"bpf" => "Binary Point File point cloud",
"bin" => "TerraScan BIN point cloud",
"qi" => "QFIT point cloud",
"sbet" => "SBET trajectory",
"csd" => "Optech CSD point cloud",
"x" => "DirectX model",
"drc" => "Draco mesh or point cloud",
"fbx" => "Autodesk FBX model",
"vtkhdf" => "VTK HDF dataset",
"e" | "ex2" | "exo" | "g" => "Exodus II dataset",
"nc" | "cdf" | "ncdf" => "NetCDF dataset",
"slpk" => "Scene Layer Package point cloud",
"abc" => "Alembic scene",
"ifc" => "Industry Foundation Classes model",
"vdb" => "OpenVDB scalar volume",
"iges" | "igs" => "IGES model",
"brep" => "Open CASCADE boundary representation",
"xbf" => "Open CASCADE XBF document",
"amf" => "Additive Manufacturing File model",
"lys" => "Lychee Slicer scene",
"tif" | "tiff" => "TIFF image or scalar volume",
"fcstd" => "FreeCAD document",
"png" => "PNG image",
"jpg" | "jpeg" => "JPEG image",
"bmp" => "BMP image",
"tga" => "TGA image",
"hdr" => "Radiance HDR image",
"webp" => "WebP image",
"exr" => "OpenEXR image",
"dcm" => "DICOM image",
"blend" => "Blender scene",
"lbrn" | "lbrn2" => "LightBurn project",
"dwg" => "AutoCAD DWG drawing",
"ipt" => "Autodesk Inventor part",
"iam" => "Autodesk Inventor assembly",
"f3d" => "Autodesk Fusion design",
"skp" => "SketchUp model",
"max" => "Autodesk 3ds Max scene",
"3dm" => "Rhino model",
"sldprt" => "SolidWorks part",
"sldasm" => "SolidWorks assembly",
_ => return None,
})
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn every_registered_alias_has_a_dedicated_display_description() {
for extension in extensions(&[
formats::MODEL_EXTENSIONS,
formats::IMAGE_EXTENSIONS,
saved_preview::EXTENSIONS,
formats::NATIVE_GEOMETRY_EXTENSIONS,
]) {
assert!(
description(&extension).is_some_and(|label| !label.trim().is_empty()),
"add a display description for registered extension .{extension}"
);
}
}
}