feat: support name property in meta and model jsons

This commit is contained in:
2025-12-15 18:45:59 +01:00
parent d08780fc14
commit 37e44e474a
17 changed files with 231 additions and 9 deletions

View File

@@ -35,14 +35,23 @@ pub fn name_for_inst<'old>(
| Middleware::CsvDir
| Middleware::ServerScriptDir
| Middleware::ClientScriptDir
| Middleware::ModuleScriptDir => Cow::Owned(new_inst.name.clone()),
| Middleware::ModuleScriptDir => {
let name = if validate_file_name(&new_inst.name).is_err() {
Cow::Owned(slugify_name(&new_inst.name))
} else {
Cow::Owned(new_inst.name.clone())
};
name
}
_ => {
let extension = extension_for_middleware(middleware);
let name = &new_inst.name;
validate_file_name(name).with_context(|| {
format!("name '{name}' is not legal to write to the file system")
})?;
Cow::Owned(format!("{name}.{extension}"))
let final_name = if validate_file_name(&new_inst.name).is_err() {
slugify_name(&new_inst.name)
} else {
new_inst.name.clone()
};
Cow::Owned(format!("{final_name}.{extension}"))
}
})
}
@@ -94,6 +103,39 @@ const INVALID_WINDOWS_NAMES: [&str; 22] = [
/// in a file's name.
const FORBIDDEN_CHARS: [char; 9] = ['<', '>', ':', '"', '/', '|', '?', '*', '\\'];
/// Slugifies a name by replacing forbidden characters with underscores
/// and ensuring the result is a valid file name
pub fn slugify_name(name: &str) -> String {
let mut result = String::with_capacity(name.len());
for ch in name.chars() {
if FORBIDDEN_CHARS.contains(&ch) {
result.push('_');
} else {
result.push(ch);
}
}
// Handle Windows reserved names by appending an underscore
let result_lower = result.to_lowercase();
for forbidden in INVALID_WINDOWS_NAMES {
if result_lower == forbidden.to_lowercase() {
result.push('_');
break;
}
}
while result.ends_with(' ') || result.ends_with('.') {
result.pop();
}
if result.is_empty() || result.chars().all(|c| c == '_') {
result = "instance".to_string();
}
result
}
/// Validates a provided file name to ensure it's allowed on the file system. An
/// error is returned if the name isn't allowed, indicating why.
/// This takes into account rules for Windows, MacOS, and Linux.

View File

@@ -28,7 +28,7 @@ use crate::{
Project,
};
pub use file_names::{extension_for_middleware, name_for_inst, validate_file_name};
pub use file_names::{extension_for_middleware, name_for_inst, slugify_name, validate_file_name};
pub use fs_snapshot::FsSnapshot;
pub use hash::*;
pub use property_filter::{filter_properties, filter_properties_preallocated};
@@ -360,6 +360,19 @@ pub fn get_best_middleware(snapshot: &SyncbackSnapshot) -> Middleware {
}
}
// If the instance name is invalid for the filesystem, use directory middleware
// to allow preserving the name in meta.json
if crate::syncback::file_names::validate_file_name(&inst.name).is_err() {
middleware = match middleware {
Middleware::ServerScript => Middleware::ServerScriptDir,
Middleware::ClientScript => Middleware::ClientScriptDir,
Middleware::ModuleScript => Middleware::ModuleScriptDir,
Middleware::Csv => Middleware::CsvDir,
Middleware::JsonModel | Middleware::Text => Middleware::Dir,
_ => middleware,
}
}
if middleware == Middleware::Rbxm {
middleware = match env::var(DEBUG_MODEL_FORMAT_VAR) {
Ok(value) if value == "1" => Middleware::Rbxmx,