From 9c6e37bc44a7dcf0d1b4c04db5020f75f1151175 Mon Sep 17 00:00:00 2001 From: Federico Terzi Date: Mon, 24 May 2021 20:58:18 +0200 Subject: [PATCH] feat(migrate): progress in the migrate implementation --- espanso-migrate/Cargo.toml | 2 + espanso-migrate/src/convert.rs | 68 +++++++++++++++++++ espanso-migrate/src/lib.rs | 16 ++++- espanso-migrate/src/load.rs | 117 +++++++++++++++++++++++++++++++++ 4 files changed, 202 insertions(+), 1 deletion(-) create mode 100644 espanso-migrate/src/convert.rs create mode 100644 espanso-migrate/src/load.rs diff --git a/espanso-migrate/Cargo.toml b/espanso-migrate/Cargo.toml index 50d61b1..92e3070 100644 --- a/espanso-migrate/Cargo.toml +++ b/espanso-migrate/Cargo.toml @@ -13,6 +13,8 @@ regex = "1.4.3" lazy_static = "1.4.0" dunce = "1.0.1" walkdir = "2.3.1" +yaml-rust = "0.4.5" +path-slash = "0.1.4" [dev-dependencies] tempdir = "0.3.7" diff --git a/espanso-migrate/src/convert.rs b/espanso-migrate/src/convert.rs new file mode 100644 index 0000000..df03fa1 --- /dev/null +++ b/espanso-migrate/src/convert.rs @@ -0,0 +1,68 @@ +/* + * This file is part of espanso. + * + * Copyright (C) 2019-2021 Federico Terzi + * + * espanso is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * espanso is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with espanso. If not, see . + */ + +use std::{cmp::Ordering, collections::HashMap, path::PathBuf}; +use yaml_rust::yaml::Hash; + +pub fn convert(input_files: HashMap) -> HashMap { + let mut output_files = HashMap::new(); + + let sorted_input_files = sort_input_files(&input_files); + + for input_path in sorted_input_files { + let yaml = input_files + .get(&input_path) + .expect("received unexpected file in input function"); + + if let Some((file_name, file_name_without_extension)) = extract_name_information(&input_path) { + println!("file: {}, {}", file_name, file_name_without_extension); + + // TODO: execute the actual conversion + + } else { + eprintln!("unable to extract filename from path: {}", input_path); + } + } + + output_files +} + +fn sort_input_files(input_files: &HashMap) -> Vec { + let mut files: Vec = input_files.iter().map(|(key, _)| key.clone()).collect(); + files.sort_by(|f1, f2| { + let f1_slashes = f1.matches("/").count(); + let f2_slashes = f2.matches("/").count(); + if f1_slashes > f2_slashes { + Ordering::Greater + } else if f1_slashes < f2_slashes { + Ordering::Less + } else { + f1.cmp(f2) + } + }); + files +} + +fn extract_name_information(path: &str) -> Option<(String, String)> { + let path_buf = PathBuf::from(path); + let file_name = path_buf.file_name()?.to_string_lossy().to_string(); + let extension = path_buf.extension()?.to_string_lossy().to_string(); + let file_name_without_extension = file_name.trim_end_matches(&format!(".{}", extension)).to_string(); + Some((file_name, file_name_without_extension)) +} \ No newline at end of file diff --git a/espanso-migrate/src/lib.rs b/espanso-migrate/src/lib.rs index 4c637d2..ac1116a 100644 --- a/espanso-migrate/src/lib.rs +++ b/espanso-migrate/src/lib.rs @@ -31,6 +31,9 @@ extern crate test_case; use anyhow::Result; use thiserror::Error; +mod convert; +mod load; + // TODO: implement // Use yaml-rust with "preserve-order" = true // Strategy: @@ -50,9 +53,17 @@ use thiserror::Error; // TODO: test case with packages +// TODO: keep other non-standard directories such as "images/" and "script/" + +// TODO: test also with non-lowercase file names + +// TODO: test packages in another directory + #[cfg(test)] mod tests { - use super::*; + use std::path::PathBuf; + +use super::*; use test_case::test_case; use include_dir::{include_dir, Dir}; @@ -60,6 +71,9 @@ mod tests { #[test_case(&BASE_CASE; "base case")] fn test_migration(test_data: &Dir) { + let input_files = load::load(&PathBuf::from(r"")).unwrap(); + convert::convert(input_files); + // TODO assert!(false); } diff --git a/espanso-migrate/src/load.rs b/espanso-migrate/src/load.rs new file mode 100644 index 0000000..932a6d4 --- /dev/null +++ b/espanso-migrate/src/load.rs @@ -0,0 +1,117 @@ +/* + * This file is part of espanso. + * + * Copyright (C) 2019-2021 Federico Terzi + * + * espanso is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * espanso is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with espanso. If not, see . + */ + +use anyhow::Result; +use path_slash::PathExt; +use std::{collections::HashMap, path::Path}; +use thiserror::Error; +use walkdir::WalkDir; +use yaml_rust::{Yaml, YamlLoader, yaml::Hash}; + +pub fn load(legacy_config_dir: &Path) -> Result> { + if !legacy_config_dir.is_dir() { + return Err(LoadError::NotDirectory.into()); + } + + let mut input_files = HashMap::new(); + + for entry in WalkDir::new(legacy_config_dir) { + match entry { + Ok(entry) => { + // Skip directories + if entry.path().is_dir() { + continue; + } + + // Skip non-yaml files + let extension = entry + .path() + .extension() + .map(|s| s.to_string_lossy().to_ascii_lowercase()) + .unwrap_or_default(); + + if extension != "yaml" && extension != "yml" { + continue; + } + + match entry.path().strip_prefix(legacy_config_dir) { + Ok(relative_path) => { + let corrected_path = relative_path.to_slash_lossy(); + + if corrected_path.is_empty() { + continue; + } + + match std::fs::read_to_string(entry.path()) { + Ok(content) => match YamlLoader::load_from_str(&content) { + Ok(mut yaml) => { + if !yaml.is_empty() { + let yaml = yaml.remove(0); + if let Yaml::Hash(hash) = yaml { + input_files.insert(corrected_path, hash); + } else { + eprintln!("yaml file does not have a valid format: {}", entry.path().display()); + } + } else { + eprintln!( + "error, found empty document while reading entry: {}", + entry.path().display() + ); + } + } + Err(err) => { + eprintln!( + "experienced error while parsing file: {}, error: {}", + entry.path().display(), + err + ); + } + }, + Err(err) => { + eprintln!( + "error while reading entry: {}, error: {}", + entry.path().display(), + err + ); + } + } + } + Err(err) => { + eprintln!( + "error while analyzing entry: {}, error: {}", + entry.path().display(), + err + ); + } + } + } + Err(err) => { + eprintln!("experienced error while reading entry: {}", err) + } + } + } + + Ok(input_files) +} + +#[derive(Error, Debug)] +pub enum LoadError { + #[error("the provided legacy_config_dir is not a directory")] + NotDirectory, +}