summaryrefslogtreecommitdiff
path: root/systems/sheet/src/mapping
diff options
context:
space:
mode:
author魏曹先生 <1992414357@qq.com>2026-02-27 06:16:23 +0800
committer魏曹先生 <1992414357@qq.com>2026-02-27 06:16:23 +0800
commit748c8a3353df887ee4b01e0e1327aa95c1c7225a (patch)
treeab84ee3fd6af53e8b03e73f9bdc5177f10606e4a /systems/sheet/src/mapping
parent53141f612ab43136b4b1db7406ac71bb97284460 (diff)
Add remote flag to IndexSource and parsing support
Diffstat (limited to 'systems/sheet/src/mapping')
-rw-r--r--systems/sheet/src/mapping/parse.rs189
-rw-r--r--systems/sheet/src/mapping/parse_test.rs252
2 files changed, 441 insertions, 0 deletions
diff --git a/systems/sheet/src/mapping/parse.rs b/systems/sheet/src/mapping/parse.rs
new file mode 100644
index 0000000..e203c96
--- /dev/null
+++ b/systems/sheet/src/mapping/parse.rs
@@ -0,0 +1,189 @@
+use just_fmt::fmt_path::fmt_path_str;
+
+use crate::{
+ index_source::IndexSource,
+ mapping::{LocalMapping, LocalMappingForward, error::ParseMappingError},
+};
+
+impl TryFrom<&str> for LocalMapping {
+ type Error = ParseMappingError;
+
+ fn try_from(s: &str) -> Result<Self, Self::Error> {
+ // Remove surrounding quotes if present
+ let s = s.trim_matches('"');
+
+ // Helper function to remove quotes from a string
+ fn remove_quotes(s: &str) -> String {
+ // Simply remove all quotes from the string
+ s.replace('"', "").trim().to_string()
+ }
+
+ // Helper function to split by operator, handling both spaced and non-spaced versions
+ fn split_by_operator<'a>(s: &'a str, operator: &'a str) -> Vec<&'a str> {
+ let mut result = Vec::new();
+ let mut start = 0;
+
+ // Find all occurrences of the operator
+ let mut search_from = 0;
+ while let Some(pos) = s[search_from..].find(operator) {
+ let actual_pos = search_from + pos;
+ result.push(&s[start..actual_pos]);
+ start = actual_pos + operator.len();
+ search_from = start;
+ }
+
+ if start < s.len() {
+ result.push(&s[start..]);
+ }
+
+ result
+ }
+
+ // Helper function to find operator position
+ fn find_operator<'a>(s: &'a str, operator: &'a str) -> Option<usize> {
+ s.find(operator)
+ }
+
+ // Try to parse "path" => "source" == "version" pattern
+ if let Some(arrow_pos) = find_operator(s, "=>") {
+ let after_arrow = &s[arrow_pos + 2..];
+ if let Some(equal_pos) = find_operator(after_arrow, "==") {
+ // Format: "path" => "source" == "version"
+ let path = remove_quotes(s[..arrow_pos].trim());
+ let middle_part = after_arrow[..equal_pos].trim();
+ let version_part = after_arrow[equal_pos + 2..].trim();
+
+ let middle = remove_quotes(middle_part);
+ let version_part_str = remove_quotes(version_part);
+
+ let val = fmt_path_str(path)
+ .map_err(|_| ParseMappingError::InvalidMapping)?
+ .split('/')
+ .map(|s| s.to_string())
+ .collect();
+
+ let source = IndexSource::try_from(middle.as_str())
+ .map_err(|_| ParseMappingError::InvalidMapping)?;
+
+ let version = version_part_str
+ .parse::<u16>()
+ .map_err(|_| ParseMappingError::InvalidMapping)?;
+
+ return Ok(LocalMapping {
+ val,
+ source,
+ forward: LocalMappingForward::Version { version },
+ });
+ }
+ }
+
+ // Split by "=>" to parse the format
+ let parts = split_by_operator(s, "=>");
+
+ match parts.len() {
+ 1 => {
+ // Check for "==" operator
+ if let Some(equal_pos) = find_operator(s, "==") {
+ // Format: "path" == "source" (when mapped_version equals version)
+ let path_raw = s[..equal_pos].trim();
+ let source_part_raw = s[equal_pos + 2..].trim();
+ let path = remove_quotes(path_raw);
+ let source_str = remove_quotes(source_part_raw);
+
+ let val = fmt_path_str(path)
+ .map_err(|_| ParseMappingError::InvalidMapping)?
+ .split('/')
+ .map(|s| s.to_string())
+ .collect();
+
+ let source = IndexSource::try_from(source_str.as_str())
+ .map_err(|_| ParseMappingError::InvalidMapping)?;
+
+ let version = source.version();
+
+ Ok(LocalMapping {
+ val,
+ source,
+ forward: LocalMappingForward::Version { version },
+ })
+ } else {
+ Err(ParseMappingError::InvalidMapping)
+ }
+ }
+ 2 => {
+ // Check if the second part contains "=="
+ if let Some(equal_pos) = find_operator(parts[1], "==") {
+ // Format: "path" => "source" == "version"
+ let path = remove_quotes(parts[0].trim());
+ let middle_part = parts[1][..equal_pos].trim();
+ let version_part = parts[1][equal_pos + 2..].trim();
+
+ let middle = remove_quotes(middle_part);
+ let version_part_str = remove_quotes(version_part);
+
+ let val = fmt_path_str(path)
+ .map_err(|_| ParseMappingError::InvalidMapping)?
+ .split('/')
+ .map(|s| s.to_string())
+ .collect();
+
+ let source = IndexSource::try_from(middle.as_str())
+ .map_err(|_| ParseMappingError::InvalidMapping)?;
+
+ let version = version_part_str
+ .parse::<u16>()
+ .map_err(|_| ParseMappingError::InvalidMapping)?;
+
+ return Ok(LocalMapping {
+ val,
+ source,
+ forward: LocalMappingForward::Version { version },
+ });
+ }
+
+ // Format: "path" => "source"
+ let path = remove_quotes(parts[0].trim());
+ let source_str = remove_quotes(parts[1].trim());
+
+ let val = fmt_path_str(path)
+ .map_err(|_| ParseMappingError::InvalidMapping)?
+ .split('/')
+ .map(|s| s.to_string())
+ .collect();
+
+ let source = IndexSource::try_from(source_str.as_str())
+ .map_err(|_| ParseMappingError::InvalidMapping)?;
+
+ Ok(LocalMapping {
+ val,
+ source,
+ forward: LocalMappingForward::Latest,
+ })
+ }
+ 3 => {
+ // Format: "path" => "source" => "sheet_name"
+ let path = remove_quotes(parts[0].trim());
+ let source_str = remove_quotes(parts[1].trim());
+ let sheet_name = remove_quotes(parts[2].trim());
+
+ let val = fmt_path_str(path)
+ .map_err(|_| ParseMappingError::InvalidMapping)?
+ .split('/')
+ .map(|s| s.to_string())
+ .collect();
+
+ let source = IndexSource::try_from(source_str.as_str())
+ .map_err(|_| ParseMappingError::InvalidMapping)?;
+
+ Ok(LocalMapping {
+ val,
+ source,
+ forward: LocalMappingForward::Ref {
+ sheet_name: sheet_name,
+ },
+ })
+ }
+ _ => Err(ParseMappingError::InvalidMapping),
+ }
+ }
+}
diff --git a/systems/sheet/src/mapping/parse_test.rs b/systems/sheet/src/mapping/parse_test.rs
new file mode 100644
index 0000000..a411360
--- /dev/null
+++ b/systems/sheet/src/mapping/parse_test.rs
@@ -0,0 +1,252 @@
+#[cfg(test)]
+mod tests {
+ use crate::{
+ index_source::IndexSource,
+ mapping::{LocalMapping, LocalMappingForward},
+ };
+
+ /// Helper macro for comparing two LocalMapping instances
+ /// Checks equality of the mappings themselves, their forward fields, and their index sources
+ macro_rules! mapping_eq {
+ ($a:expr, $b:expr) => {
+ assert_eq!($a, $b);
+ assert_eq!($a.forward, $b.forward);
+ assert_eq!($a.index_source(), $b.index_source());
+ };
+ }
+
+ #[test]
+ fn test_local_mapping_parse() {
+ let local_mapping = LocalMapping::new(
+ vec!["A.png".to_string()],
+ IndexSource::new(true, 1u32, 2u16),
+ LocalMappingForward::Latest,
+ )
+ .unwrap();
+ let local_mapping_gen = LocalMapping::try_from("\"A.png\" => \"1/2\"").unwrap();
+ mapping_eq!(local_mapping, local_mapping_gen);
+
+ let local_mapping = LocalMapping::new(
+ vec!["A.png".to_string()],
+ IndexSource::new(true, 1u32, 2u16),
+ LocalMappingForward::Version { version: 2u16 },
+ )
+ .unwrap();
+ let local_mapping_gen = LocalMapping::try_from("\"A.png\" == \"1/2\"").unwrap();
+ mapping_eq!(local_mapping, local_mapping_gen);
+
+ let local_mapping = LocalMapping::new(
+ vec!["A.png".to_string()],
+ IndexSource::new(true, 1u32, 2u16),
+ LocalMappingForward::Ref {
+ sheet_name: "ref".to_string(),
+ },
+ )
+ .unwrap();
+ let local_mapping_gen = LocalMapping::try_from("\"A.png\" => \"1/2\" => \"ref\"").unwrap();
+ mapping_eq!(local_mapping, local_mapping_gen);
+
+ let local_mapping = LocalMapping::new(
+ vec!["A.png".to_string()],
+ IndexSource::new(true, 1u32, 2u16),
+ LocalMappingForward::Version { version: 3u16 },
+ )
+ .unwrap();
+ let local_mapping_gen = LocalMapping::try_from("\"A.png\" => \"1/2\" == \"3\"").unwrap();
+ mapping_eq!(local_mapping, local_mapping_gen);
+
+ let local_mapping = LocalMapping::new(
+ vec!["A.png".to_string()],
+ IndexSource::new(false, 1u32, 2u16),
+ LocalMappingForward::Latest,
+ )
+ .unwrap();
+ let local_mapping_gen = LocalMapping::try_from("\"A.png\" => \"~1/2\"").unwrap();
+ mapping_eq!(local_mapping, local_mapping_gen);
+
+ let local_mapping = LocalMapping::new(
+ vec!["A.png".to_string()],
+ IndexSource::new(false, 1u32, 2u16),
+ LocalMappingForward::Version { version: 2u16 },
+ )
+ .unwrap();
+ let local_mapping_gen = LocalMapping::try_from("\"A.png\" == \"~1/2\"").unwrap();
+ mapping_eq!(local_mapping, local_mapping_gen);
+
+ let local_mapping = LocalMapping::new(
+ vec!["A.png".to_string()],
+ IndexSource::new(false, 1u32, 2u16),
+ LocalMappingForward::Ref {
+ sheet_name: "ref".to_string(),
+ },
+ )
+ .unwrap();
+ let local_mapping_gen = LocalMapping::try_from("\"A.png\" => \"~1/2\" => \"ref\"").unwrap();
+ mapping_eq!(local_mapping, local_mapping_gen);
+
+ let local_mapping = LocalMapping::new(
+ vec!["A.png".to_string()],
+ IndexSource::new(false, 1u32, 2u16),
+ LocalMappingForward::Version { version: 3u16 },
+ )
+ .unwrap();
+ let local_mapping_gen = LocalMapping::try_from("\"A.png\" => \"~1/2\" == \"3\"").unwrap();
+ mapping_eq!(local_mapping, local_mapping_gen);
+
+ let local_mapping = LocalMapping::new(
+ vec!["A.png".to_string()],
+ IndexSource::new(true, 1u32, 2u16),
+ LocalMappingForward::Latest,
+ )
+ .unwrap();
+ let local_mapping_gen = LocalMapping::try_from("\"A.png\"=>\"1/2\"").unwrap();
+ mapping_eq!(local_mapping, local_mapping_gen);
+
+ let local_mapping = LocalMapping::new(
+ vec!["A.png".to_string()],
+ IndexSource::new(true, 1u32, 2u16),
+ LocalMappingForward::Version { version: 2u16 },
+ )
+ .unwrap();
+ let local_mapping_gen = LocalMapping::try_from("\"A.png\"==\"1/2\"").unwrap();
+ mapping_eq!(local_mapping, local_mapping_gen);
+
+ let local_mapping = LocalMapping::new(
+ vec!["A.png".to_string()],
+ IndexSource::new(true, 1u32, 2u16),
+ LocalMappingForward::Ref {
+ sheet_name: "ref".to_string(),
+ },
+ )
+ .unwrap();
+ let local_mapping_gen = LocalMapping::try_from("\"A.png\"=>\"1/2\"=>\"ref\"").unwrap();
+ mapping_eq!(local_mapping, local_mapping_gen);
+
+ let local_mapping = LocalMapping::new(
+ vec!["A.png".to_string()],
+ IndexSource::new(true, 1u32, 2u16),
+ LocalMappingForward::Version { version: 3u16 },
+ )
+ .unwrap();
+ let local_mapping_gen = LocalMapping::try_from("\"A.png\"=>\"1/2\"==\"3\"").unwrap();
+ mapping_eq!(local_mapping, local_mapping_gen);
+
+ let local_mapping = LocalMapping::new(
+ vec!["A.png".to_string()],
+ IndexSource::new(false, 1u32, 2u16),
+ LocalMappingForward::Latest,
+ )
+ .unwrap();
+ let local_mapping_gen = LocalMapping::try_from("\"A.png\"=>\"~1/2\"").unwrap();
+ mapping_eq!(local_mapping, local_mapping_gen);
+
+ let local_mapping = LocalMapping::new(
+ vec!["A.png".to_string()],
+ IndexSource::new(false, 1u32, 2u16),
+ LocalMappingForward::Version { version: 2u16 },
+ )
+ .unwrap();
+ let local_mapping_gen = LocalMapping::try_from("\"A.png\"==\"~1/2\"").unwrap();
+ mapping_eq!(local_mapping, local_mapping_gen);
+
+ let local_mapping = LocalMapping::new(
+ vec!["A.png".to_string()],
+ IndexSource::new(false, 1u32, 2u16),
+ LocalMappingForward::Ref {
+ sheet_name: "ref".to_string(),
+ },
+ )
+ .unwrap();
+ let local_mapping_gen = LocalMapping::try_from("\"A.png\"=>\"~1/2\"=>\"ref\"").unwrap();
+ mapping_eq!(local_mapping, local_mapping_gen);
+
+ let local_mapping = LocalMapping::new(
+ vec!["A.png".to_string()],
+ IndexSource::new(false, 1u32, 2u16),
+ LocalMappingForward::Version { version: 3u16 },
+ )
+ .unwrap();
+ let local_mapping_gen = LocalMapping::try_from("\"A.png\"=>\"~1/2\"==\"3\"").unwrap();
+ mapping_eq!(local_mapping, local_mapping_gen);
+
+ //
+
+ let local_mapping = LocalMapping::new(
+ vec!["A.png".to_string()],
+ IndexSource::new(true, 1u32, 2u16),
+ LocalMappingForward::Latest,
+ )
+ .unwrap();
+ let local_mapping_gen = LocalMapping::try_from("\"A.png\" =>\"1/2\"").unwrap();
+ mapping_eq!(local_mapping, local_mapping_gen);
+
+ let local_mapping = LocalMapping::new(
+ vec!["A.png".to_string()],
+ IndexSource::new(true, 1u32, 2u16),
+ LocalMappingForward::Version { version: 2u16 },
+ )
+ .unwrap();
+ let local_mapping_gen = LocalMapping::try_from("\"A.png\"== \"1/2\"").unwrap();
+ mapping_eq!(local_mapping, local_mapping_gen);
+
+ let local_mapping = LocalMapping::new(
+ vec!["A.png".to_string()],
+ IndexSource::new(true, 1u32, 2u16),
+ LocalMappingForward::Ref {
+ sheet_name: "ref".to_string(),
+ },
+ )
+ .unwrap();
+ let local_mapping_gen =
+ LocalMapping::try_from("\"A.png\" => \"1/2\" =>\"ref\"").unwrap();
+ mapping_eq!(local_mapping, local_mapping_gen);
+
+ let local_mapping = LocalMapping::new(
+ vec!["A.png".to_string()],
+ IndexSource::new(true, 1u32, 2u16),
+ LocalMappingForward::Version { version: 3u16 },
+ )
+ .unwrap();
+ let local_mapping_gen = LocalMapping::try_from("\"A.png\"=> \"1/2\" ==\"3\"").unwrap();
+ mapping_eq!(local_mapping, local_mapping_gen);
+
+ let local_mapping = LocalMapping::new(
+ vec!["A.png".to_string()],
+ IndexSource::new(false, 1u32, 2u16),
+ LocalMappingForward::Latest,
+ )
+ .unwrap();
+ let local_mapping_gen = LocalMapping::try_from(" \"A.png\"=>\"~1/2\"").unwrap();
+ mapping_eq!(local_mapping, local_mapping_gen);
+
+ let local_mapping = LocalMapping::new(
+ vec!["A.png".to_string()],
+ IndexSource::new(false, 1u32, 2u16),
+ LocalMappingForward::Version { version: 2u16 },
+ )
+ .unwrap();
+ let local_mapping_gen = LocalMapping::try_from("\"A.png\" ==\"~1/2\" ").unwrap();
+ mapping_eq!(local_mapping, local_mapping_gen);
+
+ let local_mapping = LocalMapping::new(
+ vec!["A.png".to_string()],
+ IndexSource::new(false, 1u32, 2u16),
+ LocalMappingForward::Ref {
+ sheet_name: "ref".to_string(),
+ },
+ )
+ .unwrap();
+ let local_mapping_gen =
+ LocalMapping::try_from("\"A.png\"=> \"~1/2\"=> \"ref\"").unwrap();
+ mapping_eq!(local_mapping, local_mapping_gen);
+
+ let local_mapping = LocalMapping::new(
+ vec!["A.png".to_string()],
+ IndexSource::new(false, 1u32, 2u16),
+ LocalMappingForward::Version { version: 3u16 },
+ )
+ .unwrap();
+ let local_mapping_gen = LocalMapping::try_from("\"A.png\"=> \"~1/2\" ==\"3\"").unwrap();
+ mapping_eq!(local_mapping, local_mapping_gen);
+ }
+}