mirror of
https://github.com/rojo-rbx/rojo.git
synced 2026-10-24 15:01:38 +00:00
Implement encoding and decoding for Windows-invalid characters in file names
- Added `encode_path_name` and `decode_path_name` functions to handle encoding and decoding of invalid characters. - Updated `SyncbackRules` to include an option for encoding Windows-invalid characters during syncback. - Modified `name_for_inst` to conditionally encode invalid characters based on the new option. - Adjusted `ServeSession` and `InstanceContext` to support the new encoding feature. - Updated tests and snapshots to reflect changes in encoding behavior.
This commit is contained in:
parent
db87fa010f
commit
6d8c85682d
59 changed files with 350 additions and 53 deletions
34
src/json.rs
34
src/json.rs
|
|
@ -166,19 +166,6 @@ pub fn to_vec_pretty_sorted<T: Serialize>(value: &T) -> anyhow::Result<Vec<u8>>
|
||||||
serde_json::to_vec_pretty(&sorted_value).context("Failed to serialize to JSON")
|
serde_json::to_vec_pretty(&sorted_value).context("Failed to serialize to JSON")
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Serialize a value to a pretty-printed JSON string with sorted keys.
|
|
||||||
///
|
|
||||||
/// This ensures deterministic output by sorting all object keys alphabetically,
|
|
||||||
/// which is especially useful for version control and syncback operations.
|
|
||||||
///
|
|
||||||
/// # Errors
|
|
||||||
///
|
|
||||||
/// Returns an error if the value cannot be serialized to JSON.
|
|
||||||
pub fn to_string_pretty_sorted<T: Serialize>(value: &T) -> anyhow::Result<String> {
|
|
||||||
let json_value = serde_json::to_value(value).context("Failed to convert to JSON value")?;
|
|
||||||
let sorted_value = sort_json_value(json_value);
|
|
||||||
serde_json::to_string_pretty(&sorted_value).context("Failed to serialize to JSON")
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
@ -380,25 +367,6 @@ mod tests {
|
||||||
assert!(mango_pos < zebra_pos);
|
assert!(mango_pos < zebra_pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_to_string_pretty_sorted() {
|
|
||||||
use indexmap::IndexMap;
|
|
||||||
|
|
||||||
let mut map: IndexMap<String, i32> = IndexMap::new();
|
|
||||||
map.insert("zebra".to_string(), 1);
|
|
||||||
map.insert("apple".to_string(), 2);
|
|
||||||
map.insert("mango".to_string(), 3);
|
|
||||||
|
|
||||||
let output = to_string_pretty_sorted(&map).unwrap();
|
|
||||||
|
|
||||||
// Keys should be in alphabetical order
|
|
||||||
let apple_pos = output.find("apple").unwrap();
|
|
||||||
let mango_pos = output.find("mango").unwrap();
|
|
||||||
let zebra_pos = output.find("zebra").unwrap();
|
|
||||||
assert!(apple_pos < mango_pos);
|
|
||||||
assert!(mango_pos < zebra_pos);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_sorted_nested_objects() {
|
fn test_sorted_nested_objects() {
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
|
@ -413,7 +381,7 @@ mod tests {
|
||||||
inner.insert("a".to_string(), 2);
|
inner.insert("a".to_string(), 2);
|
||||||
|
|
||||||
let nested = Nested { inner };
|
let nested = Nested { inner };
|
||||||
let output = to_string_pretty_sorted(&nested).unwrap();
|
let output = String::from_utf8(to_vec_pretty_sorted(&nested).unwrap()).unwrap();
|
||||||
|
|
||||||
// Inner keys should also be sorted
|
// Inner keys should also be sorted
|
||||||
let a_pos = output.find("\"a\"").unwrap();
|
let a_pos = output.find("\"a\"").unwrap();
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ mod json;
|
||||||
mod lua_ast;
|
mod lua_ast;
|
||||||
mod message_queue;
|
mod message_queue;
|
||||||
mod multimap;
|
mod multimap;
|
||||||
|
pub mod path_encoding;
|
||||||
mod path_serializer;
|
mod path_serializer;
|
||||||
mod project;
|
mod project;
|
||||||
mod resolution;
|
mod resolution;
|
||||||
|
|
|
||||||
157
src/path_encoding.rs
Normal file
157
src/path_encoding.rs
Normal file
|
|
@ -0,0 +1,157 @@
|
||||||
|
//! Encoding and decoding utilities for Windows-invalid filename characters.
|
||||||
|
//!
|
||||||
|
//! Windows does not allow the following characters in file names:
|
||||||
|
//! < > : " / \ | ? *
|
||||||
|
//!
|
||||||
|
//! Windows also does not allow leading/trailing spaces in file names.
|
||||||
|
//!
|
||||||
|
//! This module provides functions to encode these characters using a %NAME%
|
||||||
|
//! format (e.g., `<` becomes `%LT%`) and decode them back.
|
||||||
|
|
||||||
|
/// Mapping of Windows-invalid characters to their encoded representations.
|
||||||
|
const CHAR_ENCODINGS: &[(&str, &str)] = &[
|
||||||
|
("<", "%LT%"),
|
||||||
|
(">", "%GT%"),
|
||||||
|
(":", "%COLON%"),
|
||||||
|
("\"", "%QUOTE%"),
|
||||||
|
("/", "%SLASH%"),
|
||||||
|
("\\", "%BACKSLASH%"),
|
||||||
|
("|", "%PIPE%"),
|
||||||
|
("?", "%QUESTION%"),
|
||||||
|
("*", "%STAR%"),
|
||||||
|
];
|
||||||
|
|
||||||
|
const SPACE_ENCODING: &str = "%SPACE%";
|
||||||
|
|
||||||
|
/// Encodes Windows-invalid characters in a file name.
|
||||||
|
///
|
||||||
|
/// This replaces characters like `<`, `>`, `:`, `"`, `/`, `\`, `|`, `?`, `*`
|
||||||
|
/// with their encoded representations like `%LT%`, `%GT%`, etc.
|
||||||
|
/// Leading and trailing spaces are encoded as `%SPACE%`.
|
||||||
|
pub fn encode_path_name(name: &str) -> String {
|
||||||
|
// Count leading and trailing spaces first (before any modifications)
|
||||||
|
let leading_spaces = name.len() - name.trim_start_matches(' ').len();
|
||||||
|
let trailing_spaces = name.len() - name.trim_end_matches(' ').len();
|
||||||
|
|
||||||
|
// Get the middle part (without leading/trailing spaces)
|
||||||
|
let middle = &name[leading_spaces..name.len() - trailing_spaces];
|
||||||
|
|
||||||
|
// Encode invalid characters in the middle part
|
||||||
|
let mut encoded_middle = middle.to_string();
|
||||||
|
for (char, encoded) in CHAR_ENCODINGS {
|
||||||
|
encoded_middle = encoded_middle.replace(char, encoded);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build final result with encoded spaces
|
||||||
|
let encoded_prefix = SPACE_ENCODING.repeat(leading_spaces);
|
||||||
|
let encoded_suffix = SPACE_ENCODING.repeat(trailing_spaces);
|
||||||
|
|
||||||
|
format!("{}{}{}", encoded_prefix, encoded_middle, encoded_suffix)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Decodes encoded Windows-invalid characters back to their original form.
|
||||||
|
///
|
||||||
|
/// This replaces encoded representations like `%LT%`, `%GT%`, etc. back
|
||||||
|
/// to their original characters `<`, `>`, etc.
|
||||||
|
/// `%SPACE%` at the start/end is decoded back to spaces.
|
||||||
|
pub fn decode_path_name(name: &str) -> String {
|
||||||
|
let mut result = name.to_string();
|
||||||
|
|
||||||
|
// Count and strip leading %SPACE%
|
||||||
|
let mut leading_spaces = 0;
|
||||||
|
while result.starts_with(SPACE_ENCODING) {
|
||||||
|
leading_spaces += 1;
|
||||||
|
result = result[SPACE_ENCODING.len()..].to_string();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Count and strip trailing %SPACE%
|
||||||
|
let mut trailing_spaces = 0;
|
||||||
|
while result.ends_with(SPACE_ENCODING) {
|
||||||
|
trailing_spaces += 1;
|
||||||
|
result = result[..result.len() - SPACE_ENCODING.len()].to_string();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decode invalid characters in the middle
|
||||||
|
for (char, encoded) in CHAR_ENCODINGS {
|
||||||
|
result = result.replace(encoded, char);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rebuild with actual spaces
|
||||||
|
let prefix = " ".repeat(leading_spaces);
|
||||||
|
let suffix = " ".repeat(trailing_spaces);
|
||||||
|
format!("{}{}{}", prefix, result, suffix)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_encode_all_chars() {
|
||||||
|
let input = r#"<>:"/\|?*"#;
|
||||||
|
let expected = "%LT%%GT%%COLON%%QUOTE%%SLASH%%BACKSLASH%%PIPE%%QUESTION%%STAR%";
|
||||||
|
assert_eq!(encode_path_name(input), expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_decode_all_chars() {
|
||||||
|
let input = "%LT%%GT%%COLON%%QUOTE%%SLASH%%BACKSLASH%%PIPE%%QUESTION%%STAR%";
|
||||||
|
let expected = r#"<>:"/\|?*"#;
|
||||||
|
assert_eq!(decode_path_name(input), expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_roundtrip() {
|
||||||
|
let original = "Test<File>With:Special\"Chars/And\\More|Stuff?Here*End";
|
||||||
|
let encoded = encode_path_name(original);
|
||||||
|
let decoded = decode_path_name(&encoded);
|
||||||
|
assert_eq!(decoded, original);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_no_special_chars() {
|
||||||
|
let input = "NormalFileName";
|
||||||
|
assert_eq!(encode_path_name(input), input);
|
||||||
|
assert_eq!(decode_path_name(input), input);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_leading_spaces() {
|
||||||
|
let input = " LeadingSpaces";
|
||||||
|
let encoded = encode_path_name(input);
|
||||||
|
assert_eq!(encoded, "%SPACE%%SPACE%LeadingSpaces");
|
||||||
|
assert_eq!(decode_path_name(&encoded), input);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_trailing_spaces() {
|
||||||
|
let input = "TrailingSpaces ";
|
||||||
|
let encoded = encode_path_name(input);
|
||||||
|
assert_eq!(encoded, "TrailingSpaces%SPACE%%SPACE%");
|
||||||
|
assert_eq!(decode_path_name(&encoded), input);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_both_spaces() {
|
||||||
|
let input = " Both Spaces ";
|
||||||
|
let encoded = encode_path_name(input);
|
||||||
|
assert_eq!(encoded, "%SPACE%Both Spaces%SPACE%");
|
||||||
|
assert_eq!(decode_path_name(&encoded), input);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_middle_spaces_unchanged() {
|
||||||
|
let input = "Middle Spaces Here";
|
||||||
|
let encoded = encode_path_name(input);
|
||||||
|
assert_eq!(encoded, "Middle Spaces Here");
|
||||||
|
assert_eq!(decode_path_name(&encoded), input);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_roundtrip_with_spaces() {
|
||||||
|
let original = " <Test> ";
|
||||||
|
let encoded = encode_path_name(original);
|
||||||
|
let decoded = decode_path_name(&encoded);
|
||||||
|
assert_eq!(decoded, original);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -106,8 +106,15 @@ impl ServeSession {
|
||||||
|
|
||||||
let root_id = tree.get_root_id();
|
let root_id = tree.get_root_id();
|
||||||
|
|
||||||
let instance_context =
|
let decode_windows_invalid_chars = root_project
|
||||||
|
.syncback_rules
|
||||||
|
.as_ref()
|
||||||
|
.map(|rules| rules.encode_windows_invalid_chars())
|
||||||
|
.unwrap_or(false);
|
||||||
|
|
||||||
|
let mut instance_context =
|
||||||
InstanceContext::with_emit_legacy_scripts(root_project.emit_legacy_scripts);
|
InstanceContext::with_emit_legacy_scripts(root_project.emit_legacy_scripts);
|
||||||
|
instance_context.set_decode_windows_invalid_chars(decode_windows_invalid_chars);
|
||||||
|
|
||||||
log::trace!("Generating snapshot of instances from VFS");
|
log::trace!("Generating snapshot of instances from VFS");
|
||||||
let snapshot = snapshot_from_vfs(&instance_context, &vfs, start_path)?;
|
let snapshot = snapshot_from_vfs(&instance_context, &vfs, start_path)?;
|
||||||
|
|
|
||||||
|
|
@ -145,6 +145,11 @@ pub struct InstanceContext {
|
||||||
pub emit_legacy_scripts: bool,
|
pub emit_legacy_scripts: bool,
|
||||||
#[serde(skip_serializing_if = "Vec::is_empty")]
|
#[serde(skip_serializing_if = "Vec::is_empty")]
|
||||||
pub sync_rules: Vec<SyncRule>,
|
pub sync_rules: Vec<SyncRule>,
|
||||||
|
/// Whether to decode Windows-invalid characters in file names.
|
||||||
|
/// Characters like `%LT%`, `%GT%`, `%COLON%`, etc. will be decoded
|
||||||
|
/// back to `<`, `>`, `:`, etc. in instance names sent to the plugin.
|
||||||
|
#[serde(default)]
|
||||||
|
pub decode_windows_invalid_chars: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl InstanceContext {
|
impl InstanceContext {
|
||||||
|
|
@ -153,6 +158,7 @@ impl InstanceContext {
|
||||||
path_ignore_rules: Arc::new(Vec::new()),
|
path_ignore_rules: Arc::new(Vec::new()),
|
||||||
emit_legacy_scripts: emit_legacy_scripts_default().unwrap(),
|
emit_legacy_scripts: emit_legacy_scripts_default().unwrap(),
|
||||||
sync_rules: Vec::new(),
|
sync_rules: Vec::new(),
|
||||||
|
decode_windows_invalid_chars: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -165,6 +171,10 @@ impl InstanceContext {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_decode_windows_invalid_chars(&mut self, decode: bool) {
|
||||||
|
self.decode_windows_invalid_chars = decode;
|
||||||
|
}
|
||||||
|
|
||||||
/// Extend the list of ignore rules in the context with the given new rules.
|
/// Extend the list of ignore rules in the context with the given new rules.
|
||||||
pub fn add_path_ignore_rules<I>(&mut self, new_rules: I)
|
pub fn add_path_ignore_rules<I>(&mut self, new_rules: I)
|
||||||
where
|
where
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ metadata:
|
||||||
relevant_paths: []
|
relevant_paths: []
|
||||||
context:
|
context:
|
||||||
emit_legacy_scripts: true
|
emit_legacy_scripts: true
|
||||||
|
decode_windows_invalid_chars: false
|
||||||
specified_id: ~
|
specified_id: ~
|
||||||
middleware: ~
|
middleware: ~
|
||||||
schema: ~
|
schema: ~
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ metadata:
|
||||||
relevant_paths: []
|
relevant_paths: []
|
||||||
context:
|
context:
|
||||||
emit_legacy_scripts: true
|
emit_legacy_scripts: true
|
||||||
|
decode_windows_invalid_chars: false
|
||||||
specified_id: ~
|
specified_id: ~
|
||||||
middleware: ~
|
middleware: ~
|
||||||
schema: ~
|
schema: ~
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ metadata:
|
||||||
relevant_paths: []
|
relevant_paths: []
|
||||||
context:
|
context:
|
||||||
emit_legacy_scripts: true
|
emit_legacy_scripts: true
|
||||||
|
decode_windows_invalid_chars: false
|
||||||
specified_id: ~
|
specified_id: ~
|
||||||
middleware: ~
|
middleware: ~
|
||||||
schema: ~
|
schema: ~
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ metadata:
|
||||||
relevant_paths: []
|
relevant_paths: []
|
||||||
context:
|
context:
|
||||||
emit_legacy_scripts: true
|
emit_legacy_scripts: true
|
||||||
|
decode_windows_invalid_chars: false
|
||||||
specified_id: ~
|
specified_id: ~
|
||||||
middleware: ~
|
middleware: ~
|
||||||
schema: ~
|
schema: ~
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ added_instances:
|
||||||
relevant_paths: []
|
relevant_paths: []
|
||||||
context:
|
context:
|
||||||
emit_legacy_scripts: true
|
emit_legacy_scripts: true
|
||||||
|
decode_windows_invalid_chars: false
|
||||||
specified_id: ~
|
specified_id: ~
|
||||||
middleware: ~
|
middleware: ~
|
||||||
schema: ~
|
schema: ~
|
||||||
|
|
|
||||||
|
|
@ -172,6 +172,11 @@ pub fn syncback_dir_no_meta<'sync>(
|
||||||
);
|
);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
// Skip instances matching ignoreTrees
|
||||||
|
if snapshot.should_ignore_tree(*new_child_ref) {
|
||||||
|
old_child_map.remove(new_child.name.as_str());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if let Some(old_child) = old_child_map.remove(new_child.name.as_str()) {
|
if let Some(old_child) = old_child_map.remove(new_child.name.as_str()) {
|
||||||
if old_child.metadata().relevant_paths.is_empty() {
|
if old_child.metadata().relevant_paths.is_empty() {
|
||||||
log::debug!(
|
log::debug!(
|
||||||
|
|
@ -223,6 +228,10 @@ pub fn syncback_dir_no_meta<'sync>(
|
||||||
);
|
);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
// Skip instances matching ignoreTrees
|
||||||
|
if snapshot.should_ignore_tree(*new_child_ref) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
children.push(snapshot.with_joined_path(*new_child_ref, None)?);
|
children.push(snapshot.with_joined_path(*new_child_ref, None)?);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,7 @@ use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
glob::Glob,
|
glob::Glob,
|
||||||
|
path_encoding::decode_path_name,
|
||||||
project::DEFAULT_PROJECT_NAMES,
|
project::DEFAULT_PROJECT_NAMES,
|
||||||
syncback::{SyncbackReturn, SyncbackSnapshot},
|
syncback::{SyncbackReturn, SyncbackSnapshot},
|
||||||
};
|
};
|
||||||
|
|
@ -74,13 +75,19 @@ pub fn snapshot_from_vfs(
|
||||||
|
|
||||||
if meta.is_dir() {
|
if meta.is_dir() {
|
||||||
let (middleware, dir_name, init_path) = get_dir_middleware(vfs, path)?;
|
let (middleware, dir_name, init_path) = get_dir_middleware(vfs, path)?;
|
||||||
|
// Decode directory name if the flag is set
|
||||||
|
let decoded_name = if context.decode_windows_invalid_chars {
|
||||||
|
decode_path_name(dir_name)
|
||||||
|
} else {
|
||||||
|
dir_name.to_string()
|
||||||
|
};
|
||||||
// TODO: Support user defined init paths
|
// TODO: Support user defined init paths
|
||||||
// If and when we do, make sure to go support it in
|
// If and when we do, make sure to go support it in
|
||||||
// `Project::set_file_name`, as right now it special-cases
|
// `Project::set_file_name`, as right now it special-cases
|
||||||
// `default.project.json` as an `init` path.
|
// `default.project.json` as an `init` path.
|
||||||
match middleware {
|
match middleware {
|
||||||
Middleware::Dir => middleware.snapshot(context, vfs, path, dir_name),
|
Middleware::Dir => middleware.snapshot(context, vfs, path, &decoded_name),
|
||||||
_ => middleware.snapshot(context, vfs, &init_path, dir_name),
|
_ => middleware.snapshot(context, vfs, &init_path, &decoded_name),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let file_name = path
|
let file_name = path
|
||||||
|
|
@ -152,19 +159,23 @@ fn snapshot_from_path(
|
||||||
vfs: &Vfs,
|
vfs: &Vfs,
|
||||||
path: &Path,
|
path: &Path,
|
||||||
) -> anyhow::Result<Option<InstanceSnapshot>> {
|
) -> anyhow::Result<Option<InstanceSnapshot>> {
|
||||||
|
// Helper to decode name if the flag is set
|
||||||
|
let decode_name = |name: &str| -> String {
|
||||||
|
if context.decode_windows_invalid_chars {
|
||||||
|
decode_path_name(name)
|
||||||
|
} else {
|
||||||
|
name.to_string()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
if let Some(rule) = context.get_user_sync_rule(path) {
|
if let Some(rule) = context.get_user_sync_rule(path) {
|
||||||
return rule
|
let name = decode_name(rule.file_name_for_path(path)?);
|
||||||
.middleware
|
return rule.middleware.snapshot(context, vfs, path, &name);
|
||||||
.snapshot(context, vfs, path, rule.file_name_for_path(path)?);
|
|
||||||
} else {
|
} else {
|
||||||
for rule in default_sync_rules() {
|
for rule in default_sync_rules() {
|
||||||
if rule.matches(path) {
|
if rule.matches(path) {
|
||||||
return rule.middleware.snapshot(
|
let name = decode_name(rule.file_name_for_path(path)?);
|
||||||
context,
|
return rule.middleware.snapshot(context, vfs, path, &name);
|
||||||
vfs,
|
|
||||||
path,
|
|
||||||
rule.file_name_for_path(path)?,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ metadata:
|
||||||
- /foo.meta.jsonc
|
- /foo.meta.jsonc
|
||||||
context:
|
context:
|
||||||
emit_legacy_scripts: true
|
emit_legacy_scripts: true
|
||||||
|
decode_windows_invalid_chars: false
|
||||||
specified_id: ~
|
specified_id: ~
|
||||||
middleware: ~
|
middleware: ~
|
||||||
schema: ~
|
schema: ~
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ metadata:
|
||||||
- /root/init.meta.jsonc
|
- /root/init.meta.jsonc
|
||||||
context:
|
context:
|
||||||
emit_legacy_scripts: true
|
emit_legacy_scripts: true
|
||||||
|
decode_windows_invalid_chars: false
|
||||||
specified_id: ~
|
specified_id: ~
|
||||||
middleware: ~
|
middleware: ~
|
||||||
schema: ~
|
schema: ~
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ metadata:
|
||||||
- /root/init.meta.jsonc
|
- /root/init.meta.jsonc
|
||||||
context:
|
context:
|
||||||
emit_legacy_scripts: true
|
emit_legacy_scripts: true
|
||||||
|
decode_windows_invalid_chars: false
|
||||||
specified_id: manually specified
|
specified_id: manually specified
|
||||||
middleware: ~
|
middleware: ~
|
||||||
schema: ~
|
schema: ~
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ metadata:
|
||||||
- /foo.meta.jsonc
|
- /foo.meta.jsonc
|
||||||
context:
|
context:
|
||||||
emit_legacy_scripts: true
|
emit_legacy_scripts: true
|
||||||
|
decode_windows_invalid_chars: false
|
||||||
specified_id: ~
|
specified_id: ~
|
||||||
middleware: ~
|
middleware: ~
|
||||||
schema: ~
|
schema: ~
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ metadata:
|
||||||
- /foo/init.meta.jsonc
|
- /foo/init.meta.jsonc
|
||||||
context:
|
context:
|
||||||
emit_legacy_scripts: true
|
emit_legacy_scripts: true
|
||||||
|
decode_windows_invalid_chars: false
|
||||||
specified_id: ~
|
specified_id: ~
|
||||||
middleware: ~
|
middleware: ~
|
||||||
schema: ~
|
schema: ~
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ metadata:
|
||||||
- /foo/init.meta.jsonc
|
- /foo/init.meta.jsonc
|
||||||
context:
|
context:
|
||||||
emit_legacy_scripts: true
|
emit_legacy_scripts: true
|
||||||
|
decode_windows_invalid_chars: false
|
||||||
specified_id: ~
|
specified_id: ~
|
||||||
middleware: ~
|
middleware: ~
|
||||||
schema: ~
|
schema: ~
|
||||||
|
|
@ -45,6 +46,7 @@ children:
|
||||||
- /foo/Child/init.meta.jsonc
|
- /foo/Child/init.meta.jsonc
|
||||||
context:
|
context:
|
||||||
emit_legacy_scripts: true
|
emit_legacy_scripts: true
|
||||||
|
decode_windows_invalid_chars: false
|
||||||
specified_id: ~
|
specified_id: ~
|
||||||
middleware: dir
|
middleware: dir
|
||||||
schema: ~
|
schema: ~
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ metadata:
|
||||||
- /foo.meta.jsonc
|
- /foo.meta.jsonc
|
||||||
context:
|
context:
|
||||||
emit_legacy_scripts: true
|
emit_legacy_scripts: true
|
||||||
|
decode_windows_invalid_chars: false
|
||||||
specified_id: ~
|
specified_id: ~
|
||||||
middleware: ~
|
middleware: ~
|
||||||
schema: ~
|
schema: ~
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ metadata:
|
||||||
- /foo.meta.jsonc
|
- /foo.meta.jsonc
|
||||||
context:
|
context:
|
||||||
emit_legacy_scripts: true
|
emit_legacy_scripts: true
|
||||||
|
decode_windows_invalid_chars: false
|
||||||
specified_id: manually specified
|
specified_id: manually specified
|
||||||
middleware: ~
|
middleware: ~
|
||||||
schema: ~
|
schema: ~
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ metadata:
|
||||||
- /foo.model.json
|
- /foo.model.json
|
||||||
context:
|
context:
|
||||||
emit_legacy_scripts: true
|
emit_legacy_scripts: true
|
||||||
|
decode_windows_invalid_chars: false
|
||||||
specified_id: ~
|
specified_id: ~
|
||||||
middleware: ~
|
middleware: ~
|
||||||
schema: ~
|
schema: ~
|
||||||
|
|
@ -26,6 +27,7 @@ children:
|
||||||
relevant_paths: []
|
relevant_paths: []
|
||||||
context:
|
context:
|
||||||
emit_legacy_scripts: true
|
emit_legacy_scripts: true
|
||||||
|
decode_windows_invalid_chars: false
|
||||||
specified_id: ~
|
specified_id: ~
|
||||||
middleware: ~
|
middleware: ~
|
||||||
schema: ~
|
schema: ~
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ metadata:
|
||||||
- /foo.model.json
|
- /foo.model.json
|
||||||
context:
|
context:
|
||||||
emit_legacy_scripts: true
|
emit_legacy_scripts: true
|
||||||
|
decode_windows_invalid_chars: false
|
||||||
specified_id: ~
|
specified_id: ~
|
||||||
middleware: ~
|
middleware: ~
|
||||||
schema: ~
|
schema: ~
|
||||||
|
|
@ -26,6 +27,7 @@ children:
|
||||||
relevant_paths: []
|
relevant_paths: []
|
||||||
context:
|
context:
|
||||||
emit_legacy_scripts: true
|
emit_legacy_scripts: true
|
||||||
|
decode_windows_invalid_chars: false
|
||||||
specified_id: ~
|
specified_id: ~
|
||||||
middleware: ~
|
middleware: ~
|
||||||
schema: ~
|
schema: ~
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ metadata:
|
||||||
- /foo.meta.jsonc
|
- /foo.meta.jsonc
|
||||||
context:
|
context:
|
||||||
emit_legacy_scripts: true
|
emit_legacy_scripts: true
|
||||||
|
decode_windows_invalid_chars: false
|
||||||
specified_id: ~
|
specified_id: ~
|
||||||
middleware: ~
|
middleware: ~
|
||||||
schema: ~
|
schema: ~
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ metadata:
|
||||||
- /foo.meta.jsonc
|
- /foo.meta.jsonc
|
||||||
context:
|
context:
|
||||||
emit_legacy_scripts: true
|
emit_legacy_scripts: true
|
||||||
|
decode_windows_invalid_chars: false
|
||||||
specified_id: ~
|
specified_id: ~
|
||||||
middleware: ~
|
middleware: ~
|
||||||
schema: ~
|
schema: ~
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ metadata:
|
||||||
- /foo.meta.jsonc
|
- /foo.meta.jsonc
|
||||||
context:
|
context:
|
||||||
emit_legacy_scripts: true
|
emit_legacy_scripts: true
|
||||||
|
decode_windows_invalid_chars: false
|
||||||
specified_id: ~
|
specified_id: ~
|
||||||
middleware: ~
|
middleware: ~
|
||||||
schema: ~
|
schema: ~
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ metadata:
|
||||||
- /bar.meta.jsonc
|
- /bar.meta.jsonc
|
||||||
context:
|
context:
|
||||||
emit_legacy_scripts: true
|
emit_legacy_scripts: true
|
||||||
|
decode_windows_invalid_chars: false
|
||||||
specified_id: ~
|
specified_id: ~
|
||||||
middleware: ~
|
middleware: ~
|
||||||
schema: ~
|
schema: ~
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ metadata:
|
||||||
- /foo.meta.jsonc
|
- /foo.meta.jsonc
|
||||||
context:
|
context:
|
||||||
emit_legacy_scripts: true
|
emit_legacy_scripts: true
|
||||||
|
decode_windows_invalid_chars: false
|
||||||
specified_id: ~
|
specified_id: ~
|
||||||
middleware: ~
|
middleware: ~
|
||||||
schema: ~
|
schema: ~
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ metadata:
|
||||||
- /foo.meta.jsonc
|
- /foo.meta.jsonc
|
||||||
context:
|
context:
|
||||||
emit_legacy_scripts: true
|
emit_legacy_scripts: true
|
||||||
|
decode_windows_invalid_chars: false
|
||||||
specified_id: ~
|
specified_id: ~
|
||||||
middleware: ~
|
middleware: ~
|
||||||
schema: ~
|
schema: ~
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ metadata:
|
||||||
- /root/init.meta.jsonc
|
- /root/init.meta.jsonc
|
||||||
context:
|
context:
|
||||||
emit_legacy_scripts: true
|
emit_legacy_scripts: true
|
||||||
|
decode_windows_invalid_chars: false
|
||||||
specified_id: ~
|
specified_id: ~
|
||||||
middleware: ~
|
middleware: ~
|
||||||
schema: ~
|
schema: ~
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ metadata:
|
||||||
- /root/init.meta.jsonc
|
- /root/init.meta.jsonc
|
||||||
context:
|
context:
|
||||||
emit_legacy_scripts: true
|
emit_legacy_scripts: true
|
||||||
|
decode_windows_invalid_chars: false
|
||||||
specified_id: manually specified
|
specified_id: manually specified
|
||||||
middleware: ~
|
middleware: ~
|
||||||
schema: ~
|
schema: ~
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ metadata:
|
||||||
- /foo.meta.jsonc
|
- /foo.meta.jsonc
|
||||||
context:
|
context:
|
||||||
emit_legacy_scripts: false
|
emit_legacy_scripts: false
|
||||||
|
decode_windows_invalid_chars: false
|
||||||
specified_id: ~
|
specified_id: ~
|
||||||
middleware: ~
|
middleware: ~
|
||||||
schema: ~
|
schema: ~
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ metadata:
|
||||||
- /foo.meta.jsonc
|
- /foo.meta.jsonc
|
||||||
context:
|
context:
|
||||||
emit_legacy_scripts: false
|
emit_legacy_scripts: false
|
||||||
|
decode_windows_invalid_chars: false
|
||||||
specified_id: ~
|
specified_id: ~
|
||||||
middleware: ~
|
middleware: ~
|
||||||
schema: ~
|
schema: ~
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ metadata:
|
||||||
- /foo.meta.jsonc
|
- /foo.meta.jsonc
|
||||||
context:
|
context:
|
||||||
emit_legacy_scripts: false
|
emit_legacy_scripts: false
|
||||||
|
decode_windows_invalid_chars: false
|
||||||
specified_id: ~
|
specified_id: ~
|
||||||
middleware: ~
|
middleware: ~
|
||||||
schema: ~
|
schema: ~
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ metadata:
|
||||||
- /foo.meta.jsonc
|
- /foo.meta.jsonc
|
||||||
context:
|
context:
|
||||||
emit_legacy_scripts: false
|
emit_legacy_scripts: false
|
||||||
|
decode_windows_invalid_chars: false
|
||||||
specified_id: ~
|
specified_id: ~
|
||||||
middleware: ~
|
middleware: ~
|
||||||
schema: ~
|
schema: ~
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ metadata:
|
||||||
- /bar.meta.jsonc
|
- /bar.meta.jsonc
|
||||||
context:
|
context:
|
||||||
emit_legacy_scripts: false
|
emit_legacy_scripts: false
|
||||||
|
decode_windows_invalid_chars: false
|
||||||
specified_id: ~
|
specified_id: ~
|
||||||
middleware: ~
|
middleware: ~
|
||||||
schema: ~
|
schema: ~
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ metadata:
|
||||||
- /foo.meta.jsonc
|
- /foo.meta.jsonc
|
||||||
context:
|
context:
|
||||||
emit_legacy_scripts: false
|
emit_legacy_scripts: false
|
||||||
|
decode_windows_invalid_chars: false
|
||||||
specified_id: ~
|
specified_id: ~
|
||||||
middleware: ~
|
middleware: ~
|
||||||
schema: ~
|
schema: ~
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ metadata:
|
||||||
- /foo.meta.jsonc
|
- /foo.meta.jsonc
|
||||||
context:
|
context:
|
||||||
emit_legacy_scripts: false
|
emit_legacy_scripts: false
|
||||||
|
decode_windows_invalid_chars: false
|
||||||
specified_id: ~
|
specified_id: ~
|
||||||
middleware: ~
|
middleware: ~
|
||||||
schema: ~
|
schema: ~
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ metadata:
|
||||||
- /foo/bar.meta.jsonc
|
- /foo/bar.meta.jsonc
|
||||||
context:
|
context:
|
||||||
emit_legacy_scripts: true
|
emit_legacy_scripts: true
|
||||||
|
decode_windows_invalid_chars: false
|
||||||
specified_id: manually specified
|
specified_id: manually specified
|
||||||
middleware: ~
|
middleware: ~
|
||||||
schema: ~
|
schema: ~
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ metadata:
|
||||||
- /foo/bar.meta.jsonc
|
- /foo/bar.meta.jsonc
|
||||||
context:
|
context:
|
||||||
emit_legacy_scripts: true
|
emit_legacy_scripts: true
|
||||||
|
decode_windows_invalid_chars: false
|
||||||
specified_id: manually specified
|
specified_id: manually specified
|
||||||
middleware: ~
|
middleware: ~
|
||||||
schema: ~
|
schema: ~
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ metadata:
|
||||||
- /foo/init.meta.jsonc
|
- /foo/init.meta.jsonc
|
||||||
context:
|
context:
|
||||||
emit_legacy_scripts: true
|
emit_legacy_scripts: true
|
||||||
|
decode_windows_invalid_chars: false
|
||||||
specified_id: manually specified
|
specified_id: manually specified
|
||||||
middleware: ~
|
middleware: ~
|
||||||
schema: ~
|
schema: ~
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ metadata:
|
||||||
- /foo/init.meta.jsonc
|
- /foo/init.meta.jsonc
|
||||||
context:
|
context:
|
||||||
emit_legacy_scripts: true
|
emit_legacy_scripts: true
|
||||||
|
decode_windows_invalid_chars: false
|
||||||
specified_id: manually specified
|
specified_id: manually specified
|
||||||
middleware: ~
|
middleware: ~
|
||||||
schema: ~
|
schema: ~
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ metadata:
|
||||||
- /foo/default.project.json
|
- /foo/default.project.json
|
||||||
context:
|
context:
|
||||||
emit_legacy_scripts: true
|
emit_legacy_scripts: true
|
||||||
|
decode_windows_invalid_chars: false
|
||||||
specified_id: ~
|
specified_id: ~
|
||||||
middleware: ~
|
middleware: ~
|
||||||
schema: ~
|
schema: ~
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ metadata:
|
||||||
- /foo/hello.project.json
|
- /foo/hello.project.json
|
||||||
context:
|
context:
|
||||||
emit_legacy_scripts: true
|
emit_legacy_scripts: true
|
||||||
|
decode_windows_invalid_chars: false
|
||||||
specified_id: ~
|
specified_id: ~
|
||||||
middleware: ~
|
middleware: ~
|
||||||
schema: ~
|
schema: ~
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ metadata:
|
||||||
- /foo/default.project.json
|
- /foo/default.project.json
|
||||||
context:
|
context:
|
||||||
emit_legacy_scripts: true
|
emit_legacy_scripts: true
|
||||||
|
decode_windows_invalid_chars: false
|
||||||
specified_id: ~
|
specified_id: ~
|
||||||
middleware: project
|
middleware: project
|
||||||
schema: ~
|
schema: ~
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ metadata:
|
||||||
- /foo.project.json
|
- /foo.project.json
|
||||||
context:
|
context:
|
||||||
emit_legacy_scripts: true
|
emit_legacy_scripts: true
|
||||||
|
decode_windows_invalid_chars: false
|
||||||
specified_id: ~
|
specified_id: ~
|
||||||
middleware: ~
|
middleware: ~
|
||||||
schema: ~
|
schema: ~
|
||||||
|
|
@ -31,6 +32,7 @@ children:
|
||||||
relevant_paths: []
|
relevant_paths: []
|
||||||
context:
|
context:
|
||||||
emit_legacy_scripts: true
|
emit_legacy_scripts: true
|
||||||
|
decode_windows_invalid_chars: false
|
||||||
specified_id: ~
|
specified_id: ~
|
||||||
middleware: ~
|
middleware: ~
|
||||||
schema: ~
|
schema: ~
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ metadata:
|
||||||
- /foo/default.project.json
|
- /foo/default.project.json
|
||||||
context:
|
context:
|
||||||
emit_legacy_scripts: true
|
emit_legacy_scripts: true
|
||||||
|
decode_windows_invalid_chars: false
|
||||||
specified_id: ~
|
specified_id: ~
|
||||||
middleware: project
|
middleware: project
|
||||||
schema: ~
|
schema: ~
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ metadata:
|
||||||
- /foo/default.project.json
|
- /foo/default.project.json
|
||||||
context:
|
context:
|
||||||
emit_legacy_scripts: true
|
emit_legacy_scripts: true
|
||||||
|
decode_windows_invalid_chars: false
|
||||||
specified_id: ~
|
specified_id: ~
|
||||||
middleware: project
|
middleware: project
|
||||||
schema: ~
|
schema: ~
|
||||||
|
|
@ -32,6 +33,7 @@ children:
|
||||||
relevant_paths: []
|
relevant_paths: []
|
||||||
context:
|
context:
|
||||||
emit_legacy_scripts: true
|
emit_legacy_scripts: true
|
||||||
|
decode_windows_invalid_chars: false
|
||||||
specified_id: ~
|
specified_id: ~
|
||||||
middleware: ~
|
middleware: ~
|
||||||
schema: ~
|
schema: ~
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ metadata:
|
||||||
- /foo/default.project.json
|
- /foo/default.project.json
|
||||||
context:
|
context:
|
||||||
emit_legacy_scripts: true
|
emit_legacy_scripts: true
|
||||||
|
decode_windows_invalid_chars: false
|
||||||
specified_id: ~
|
specified_id: ~
|
||||||
middleware: text
|
middleware: text
|
||||||
schema: ~
|
schema: ~
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ metadata:
|
||||||
- /foo.project.json
|
- /foo.project.json
|
||||||
context:
|
context:
|
||||||
emit_legacy_scripts: true
|
emit_legacy_scripts: true
|
||||||
|
decode_windows_invalid_chars: false
|
||||||
specified_id: ~
|
specified_id: ~
|
||||||
middleware: ~
|
middleware: ~
|
||||||
schema: ~
|
schema: ~
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ metadata:
|
||||||
- /foo.project.json
|
- /foo.project.json
|
||||||
context:
|
context:
|
||||||
emit_legacy_scripts: true
|
emit_legacy_scripts: true
|
||||||
|
decode_windows_invalid_chars: false
|
||||||
specified_id: ~
|
specified_id: ~
|
||||||
middleware: ~
|
middleware: ~
|
||||||
schema: ~
|
schema: ~
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ metadata:
|
||||||
- /foo.meta.jsonc
|
- /foo.meta.jsonc
|
||||||
context:
|
context:
|
||||||
emit_legacy_scripts: true
|
emit_legacy_scripts: true
|
||||||
|
decode_windows_invalid_chars: false
|
||||||
specified_id: ~
|
specified_id: ~
|
||||||
middleware: ~
|
middleware: ~
|
||||||
schema: ~
|
schema: ~
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ metadata:
|
||||||
- foo.meta.jsonc
|
- foo.meta.jsonc
|
||||||
context:
|
context:
|
||||||
emit_legacy_scripts: true
|
emit_legacy_scripts: true
|
||||||
|
decode_windows_invalid_chars: false
|
||||||
specified_id: manually specified
|
specified_id: manually specified
|
||||||
middleware: ~
|
middleware: ~
|
||||||
schema: ~
|
schema: ~
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ metadata:
|
||||||
- /foo.meta.jsonc
|
- /foo.meta.jsonc
|
||||||
context:
|
context:
|
||||||
emit_legacy_scripts: true
|
emit_legacy_scripts: true
|
||||||
|
decode_windows_invalid_chars: false
|
||||||
specified_id: ~
|
specified_id: ~
|
||||||
middleware: ~
|
middleware: ~
|
||||||
schema: ~
|
schema: ~
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ metadata:
|
||||||
- /foo.meta.jsonc
|
- /foo.meta.jsonc
|
||||||
context:
|
context:
|
||||||
emit_legacy_scripts: true
|
emit_legacy_scripts: true
|
||||||
|
decode_windows_invalid_chars: false
|
||||||
specified_id: manually specified
|
specified_id: manually specified
|
||||||
middleware: ~
|
middleware: ~
|
||||||
schema: ~
|
schema: ~
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ metadata:
|
||||||
- /foo.meta.jsonc
|
- /foo.meta.jsonc
|
||||||
context:
|
context:
|
||||||
emit_legacy_scripts: true
|
emit_legacy_scripts: true
|
||||||
|
decode_windows_invalid_chars: false
|
||||||
specified_id: ~
|
specified_id: ~
|
||||||
middleware: ~
|
middleware: ~
|
||||||
schema: ~
|
schema: ~
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ metadata:
|
||||||
- foo.meta.jsonc
|
- foo.meta.jsonc
|
||||||
context:
|
context:
|
||||||
emit_legacy_scripts: true
|
emit_legacy_scripts: true
|
||||||
|
decode_windows_invalid_chars: false
|
||||||
specified_id: manually specified
|
specified_id: manually specified
|
||||||
middleware: ~
|
middleware: ~
|
||||||
schema: ~
|
schema: ~
|
||||||
|
|
|
||||||
|
|
@ -6,12 +6,15 @@ use std::borrow::Cow;
|
||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
use rbx_dom_weak::Instance;
|
use rbx_dom_weak::Instance;
|
||||||
|
|
||||||
use crate::{snapshot::InstanceWithMeta, snapshot_middleware::Middleware};
|
use crate::{
|
||||||
|
path_encoding::encode_path_name, snapshot::InstanceWithMeta, snapshot_middleware::Middleware,
|
||||||
|
};
|
||||||
|
|
||||||
pub fn name_for_inst<'old>(
|
pub fn name_for_inst<'old>(
|
||||||
middleware: Middleware,
|
middleware: Middleware,
|
||||||
new_inst: &Instance,
|
new_inst: &Instance,
|
||||||
old_inst: Option<InstanceWithMeta<'old>>,
|
old_inst: Option<InstanceWithMeta<'old>>,
|
||||||
|
encode_invalid_chars: bool,
|
||||||
) -> anyhow::Result<Cow<'old, str>> {
|
) -> anyhow::Result<Cow<'old, str>> {
|
||||||
if let Some(old_inst) = old_inst {
|
if let Some(old_inst) = old_inst {
|
||||||
if let Some(source) = old_inst.metadata().relevant_paths.first() {
|
if let Some(source) = old_inst.metadata().relevant_paths.first() {
|
||||||
|
|
@ -35,13 +38,37 @@ pub fn name_for_inst<'old>(
|
||||||
| Middleware::CsvDir
|
| Middleware::CsvDir
|
||||||
| Middleware::ServerScriptDir
|
| Middleware::ServerScriptDir
|
||||||
| Middleware::ClientScriptDir
|
| Middleware::ClientScriptDir
|
||||||
| Middleware::ModuleScriptDir => Cow::Owned(new_inst.name.clone()),
|
| Middleware::ModuleScriptDir => {
|
||||||
|
let name = if encode_invalid_chars {
|
||||||
|
let encoded = encode_path_name(&new_inst.name);
|
||||||
|
// Validate for issues not fixed by encoding (trailing space/dot, control chars, reserved names)
|
||||||
|
validate_file_name(&encoded).with_context(|| {
|
||||||
|
format!("name '{}' (encoded from '{}') is not legal to write to the file system", encoded, new_inst.name)
|
||||||
|
})?;
|
||||||
|
encoded
|
||||||
|
} else {
|
||||||
|
validate_file_name(&new_inst.name).with_context(|| {
|
||||||
|
format!("name '{}' is not legal to write to the file system", new_inst.name)
|
||||||
|
})?;
|
||||||
|
new_inst.name.clone()
|
||||||
|
};
|
||||||
|
Cow::Owned(name)
|
||||||
|
}
|
||||||
_ => {
|
_ => {
|
||||||
let extension = extension_for_middleware(middleware);
|
let extension = extension_for_middleware(middleware);
|
||||||
let name = &new_inst.name;
|
let name = if encode_invalid_chars {
|
||||||
validate_file_name(name).with_context(|| {
|
let encoded = encode_path_name(&new_inst.name);
|
||||||
format!("name '{name}' is not legal to write to the file system")
|
// Validate for issues not fixed by encoding (trailing space/dot, control chars, reserved names)
|
||||||
})?;
|
validate_file_name(&encoded).with_context(|| {
|
||||||
|
format!("name '{}' (encoded from '{}') is not legal to write to the file system", encoded, new_inst.name)
|
||||||
|
})?;
|
||||||
|
encoded
|
||||||
|
} else {
|
||||||
|
validate_file_name(&new_inst.name).with_context(|| {
|
||||||
|
format!("name '{}' is not legal to write to the file system", new_inst.name)
|
||||||
|
})?;
|
||||||
|
new_inst.name.clone()
|
||||||
|
};
|
||||||
Cow::Owned(format!("{name}.{extension}"))
|
Cow::Owned(format!("{name}.{extension}"))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -409,6 +409,12 @@ pub struct SyncbackRules {
|
||||||
/// generally a better UX.
|
/// generally a better UX.
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
create_ignore_dir_paths: Option<bool>,
|
create_ignore_dir_paths: Option<bool>,
|
||||||
|
/// Whether to encode Windows-invalid characters in file names during
|
||||||
|
/// syncback. Characters like `<`, `>`, `:`, `"`, `/`, `\`, `|`, `?`, `*`
|
||||||
|
/// will be encoded as `%LT%`, `%GT%`, `%COLON%`, etc.
|
||||||
|
/// Defaults to `false`.
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
encode_windows_invalid_chars: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SyncbackRules {
|
impl SyncbackRules {
|
||||||
|
|
@ -432,6 +438,13 @@ impl SyncbackRules {
|
||||||
|
|
||||||
Ok(globs)
|
Ok(globs)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns whether Windows-invalid characters should be encoded in file
|
||||||
|
/// names during syncback. Defaults to `false`.
|
||||||
|
#[inline]
|
||||||
|
pub fn encode_windows_invalid_chars(&self) -> bool {
|
||||||
|
self.encode_windows_invalid_chars.unwrap_or(false)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_valid_path(globs: &Option<Vec<Glob>>, base_path: &Path, path: &Path) -> bool {
|
fn is_valid_path(globs: &Option<Vec<Glob>>, base_path: &Path, path: &Path) -> bool {
|
||||||
|
|
|
||||||
|
|
@ -44,7 +44,13 @@ impl<'sync> SyncbackSnapshot<'sync> {
|
||||||
middleware: None,
|
middleware: None,
|
||||||
};
|
};
|
||||||
let middleware = get_best_middleware(&snapshot);
|
let middleware = get_best_middleware(&snapshot);
|
||||||
let name = name_for_inst(middleware, snapshot.new_inst(), snapshot.old_inst())?;
|
let encode_invalid_chars = self.encode_windows_invalid_chars();
|
||||||
|
let name = name_for_inst(
|
||||||
|
middleware,
|
||||||
|
snapshot.new_inst(),
|
||||||
|
snapshot.old_inst(),
|
||||||
|
encode_invalid_chars,
|
||||||
|
)?;
|
||||||
snapshot.path = self.path.join(name.as_ref());
|
snapshot.path = self.path.join(name.as_ref());
|
||||||
|
|
||||||
Ok(snapshot)
|
Ok(snapshot)
|
||||||
|
|
@ -70,7 +76,13 @@ impl<'sync> SyncbackSnapshot<'sync> {
|
||||||
middleware: None,
|
middleware: None,
|
||||||
};
|
};
|
||||||
let middleware = get_best_middleware(&snapshot);
|
let middleware = get_best_middleware(&snapshot);
|
||||||
let name = name_for_inst(middleware, snapshot.new_inst(), snapshot.old_inst())?;
|
let encode_invalid_chars = self.encode_windows_invalid_chars();
|
||||||
|
let name = name_for_inst(
|
||||||
|
middleware,
|
||||||
|
snapshot.new_inst(),
|
||||||
|
snapshot.old_inst(),
|
||||||
|
encode_invalid_chars,
|
||||||
|
)?;
|
||||||
snapshot.path = base_path.join(name.as_ref());
|
snapshot.path = base_path.join(name.as_ref());
|
||||||
|
|
||||||
Ok(snapshot)
|
Ok(snapshot)
|
||||||
|
|
@ -224,6 +236,34 @@ impl<'sync> SyncbackSnapshot<'sync> {
|
||||||
.map(|classes| classes.iter().any(|c| c == class_name))
|
.map(|classes| classes.iter().any(|c| c == class_name))
|
||||||
.unwrap_or(false)
|
.unwrap_or(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Checks if an instance should be ignored based on ignoreTrees rules.
|
||||||
|
/// Takes a Ref to check against the new tree's instance path.
|
||||||
|
#[inline]
|
||||||
|
pub fn should_ignore_tree(&self, referent: Ref) -> bool {
|
||||||
|
if let Some(ignore_trees) = self.ignore_tree() {
|
||||||
|
let path = inst_path(self.new_tree(), referent);
|
||||||
|
for ignored in ignore_trees {
|
||||||
|
if path.starts_with(ignored.as_str()) {
|
||||||
|
log::debug!("Ignoring {path} because it matches ignoreTrees entry: {ignored}");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns whether Windows-invalid characters should be encoded in file
|
||||||
|
/// names during syncback.
|
||||||
|
#[inline]
|
||||||
|
pub fn encode_windows_invalid_chars(&self) -> bool {
|
||||||
|
self.data
|
||||||
|
.project
|
||||||
|
.syncback_rules
|
||||||
|
.as_ref()
|
||||||
|
.map(|rules| rules.encode_windows_invalid_chars())
|
||||||
|
.unwrap_or(false)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn filter_out_property(inst: &Instance, prop_name: &str) -> bool {
|
pub fn filter_out_property(inst: &Instance, prop_name: &str) -> bool {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue