diff --git a/backend-local-server/src/bin/diffedit3-web.rs b/backend-local-server/src/bin/diffedit3-web.rs index 14226a9..ac97541 100644 --- a/backend-local-server/src/bin/diffedit3-web.rs +++ b/backend-local-server/src/bin/diffedit3-web.rs @@ -42,7 +42,7 @@ fn exit_with_cli_error(s: String) -> ! { #[tokio::main] async fn main() -> Result<(), MergeToolError> { let cli = LocalServerCli::parse(); - let input: diffedit3::fs::ThreeDirInput = match cli.lib_cli.try_into() { + let input: Box = match cli.lib_cli.into_data_interface() { Ok(i) => i, Err(err) => { exit_with_cli_error(err.to_string()); diff --git a/backend-local-server/src/fs.rs b/backend-local-server/src/fs.rs index f1016c2..fa05bba 100644 --- a/backend-local-server/src/fs.rs +++ b/backend-local-server/src/fs.rs @@ -2,57 +2,28 @@ use std::path::{Path, PathBuf}; use walkdir::{DirEntry, WalkDir}; -use crate::{DataInterface, DataReadError, DataSaveError, EntriesToCompare, FileEntry}; - -fn scan(root: &Path) -> impl Iterator> { - // As an alternative to WalkDir, see - // https://github.com/martinvonz/jj/blob/af8eb3fd74956effee00acf00011ff0413607213/lib/src/local_working_copy.rs#L849 - WalkDir::new(root) - .into_iter() - .filter_map(|e| e.ok()) - .filter(|e| e.file_type().is_file()) - .map(|e| Ok((e.clone(), std::fs::read_to_string(e.path())?))) -} +use crate::{DataInterface, DataReadError, DataSaveError, EntriesToCompare, FakeData, FileEntry}; #[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] -pub enum ThreeDirInput { - // TODO: Separate FakeData - FakeData, - Dirs { - left: PathBuf, - right: PathBuf, - edit: PathBuf, - }, +pub struct ThreeDirInput { + left: PathBuf, + right: PathBuf, + edit: PathBuf, } impl DataInterface for ThreeDirInput { // TODO: A more efficient `get_valid_entries` implementation fn scan(&self) -> Result { - match self { - Self::FakeData => Ok(fake_data()), - Self::Dirs { left, right, edit } => scan_several([left, right, edit]), - } + let Self { left, right, edit } = self; + scan_several([left, right, edit]) } fn save_unchecked( &mut self, result: indexmap::IndexMap, ) -> Result<(), DataSaveError> { - let outdir = match self { - Self::FakeData => { - eprintln!("Can't save fake demo data. Here it is as TOML"); - eprintln!(); - eprintln!( - "{}", - toml::to_string(&result) - .unwrap_or_else(|err| format!("Failed to parse TOML: {err}")) - ); - return Err(DataSaveError::CannotSaveFakeData); - } - Self::Dirs { edit, .. } => edit, - }; - + let Self { edit: outdir, .. } = self; for (relpath, contents) in result.into_iter() { let relpath = PathBuf::from(relpath); let path = outdir.join(relpath); @@ -81,23 +52,22 @@ pub struct Cli { demo: bool, } -impl TryInto for Cli { - type Error = String; - fn try_into(self) -> Result { +impl Cli { + pub fn into_data_interface(self) -> Result, String> { if self.demo { - Ok(ThreeDirInput::FakeData) + Ok(Box::new(FakeData)) } else { match self.dirs.as_slice() { - [left, right, output] => Ok(ThreeDirInput::Dirs { + [left, right, output] => Ok(Box::new(ThreeDirInput { left: left.to_path_buf(), right: right.to_path_buf(), edit: output.to_path_buf(), - }), - [left, right] => Ok(ThreeDirInput::Dirs { + })), + [left, right] => Ok(Box::new(ThreeDirInput { left: left.to_path_buf(), right: right.to_path_buf(), edit: right.to_path_buf(), - }), + })), _ => Err(format!( "Must have 2 or 3 dirs to compare, got {} dirs instead", self.dirs.len() @@ -107,55 +77,14 @@ impl TryInto for Cli { } } -pub fn fake_data() -> EntriesToCompare { - // let mut two_sides_map = btreemap! { - // "edited_file" => [ - // Some("First\nThird\nFourth\nFourthAndAHalf\n\nFifth\nSixth\n----\ - // none two"), Some("First\nSecond\nThird\nFifth\nSixth\n----\ - // none\n") ], - // "deleted_file" => [Some("deleted"), None], - // "added file" => [None, Some("added")] - // }; - let two_sides_map = vec![ - ( - "edited_file", - [ - FileEntry::Text( - "First\nThird\nFourth\nFourthAndAHalf\n\nFifth\nSixth\n----\none two" - .to_string(), - ), - FileEntry::Text("First\nSecond\nThird\nFifth\nSixth\n----\none\n".to_string()), - ], - ), - ( - "deleted_file", - [FileEntry::Text("deleted".to_string()), FileEntry::Missing], - ), - ( - "added file", - [FileEntry::Missing, FileEntry::Text("added".to_string())], - ), - ( - "unsupported-left", - [ - FileEntry::Unsupported("demo of an unsupported file".to_string()), - FileEntry::Text("text".to_string()), - ], - ), - ( - "unsupported-right", - [ - FileEntry::Text("text".to_string()), - FileEntry::Unsupported("demo of an unsupported file".to_string()), - ], - ), - ]; - EntriesToCompare( - two_sides_map - .into_iter() - .map(|(key, [left, right])| (PathBuf::from(key), [left, right.clone(), right])) - .collect(), - ) +fn scan(root: &Path) -> impl Iterator> { + // As an alternative to WalkDir, see + // https://github.com/martinvonz/jj/blob/af8eb3fd74956effee00acf00011ff0413607213/lib/src/local_working_copy.rs#L849 + WalkDir::new(root) + .into_iter() + .filter_map(|e| e.ok()) + .filter(|e| e.file_type().is_file()) + .map(|e| Ok((e.clone(), std::fs::read_to_string(e.path())?))) } // pub fn scan_several(roots: [&Path; N]) -> diff --git a/backend-local-server/src/lib.rs b/backend-local-server/src/lib.rs index 7acebe3..b37c0e6 100644 --- a/backend-local-server/src/lib.rs +++ b/backend-local-server/src/lib.rs @@ -1,31 +1,6 @@ pub mod fs; -pub mod types; pub mod local_server; +pub mod types; pub use fs::{Cli, ThreeDirInput}; pub use types::*; - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn it_works() { - insta::assert_yaml_snapshot!(ThreeDirInput::FakeData.scan().unwrap(), - @r###" - --- - added file: - - ~ - - added - - added - deleted_file: - - deleted - - ~ - - ~ - edited_file: - - "First\nThird\nFourth\nFourthAndAHalf\n\nFifth\nSixth\n----\none two" - - "First\nSecond\nThird\nFifth\nSixth\n----\none\n" - - "First\nSecond\nThird\nFifth\nSixth\n----\none\n" - "###); - } -} diff --git a/backend-local-server/src/local_server.rs b/backend-local-server/src/local_server.rs index f42a370..12a9aa3 100644 --- a/backend-local-server/src/local_server.rs +++ b/backend-local-server/src/local_server.rs @@ -15,7 +15,7 @@ use poem::{handler, EndpointExt, Result, Route, Server}; use thiserror::Error; use crate::DataInterface; -type DataInterfacePointer = Arc>; +type DataInterfacePointer = Arc>>; #[derive(rust_embed::RustEmbed)] #[folder = "../webapp/dist"] @@ -130,7 +130,7 @@ fn acceptor_to_socket_address( } pub async fn run_server( - input: impl crate::DataInterface, + input: Box, min_port: usize, max_port: usize, open_browser: bool, diff --git a/backend-local-server/src/types.rs b/backend-local-server/src/types.rs index 808e134..c6ce612 100644 --- a/backend-local-server/src/types.rs +++ b/backend-local-server/src/types.rs @@ -128,3 +128,96 @@ impl DataInterface for EntriesToCompare { Ok(()) } } + +pub struct FakeData; + +impl DataInterface for FakeData { + fn scan(&self) -> Result { + // let mut two_sides_map = btreemap! { + // "edited_file" => [ + // Some("First\nThird\nFourth\nFourthAndAHalf\n\nFifth\nSixth\n----\ + // none two"), Some("First\nSecond\nThird\nFifth\nSixth\n----\ + // none\n") ], + // "deleted_file" => [Some("deleted"), None], + // "added file" => [None, Some("added")] + // }; + let two_sides_map = vec![ + ( + "edited_file", + [ + FileEntry::Text( + "First\nThird\nFourth\nFourthAndAHalf\n\nFifth\nSixth\n----\none two" + .to_string(), + ), + FileEntry::Text("First\nSecond\nThird\nFifth\nSixth\n----\none\n".to_string()), + ], + ), + ( + "deleted_file", + [FileEntry::Text("deleted".to_string()), FileEntry::Missing], + ), + ( + "added file", + [FileEntry::Missing, FileEntry::Text("added".to_string())], + ), + ( + "unsupported-left", + [ + FileEntry::Unsupported("demo of an unsupported file".to_string()), + FileEntry::Text("text".to_string()), + ], + ), + ( + "unsupported-right", + [ + FileEntry::Text("text".to_string()), + FileEntry::Unsupported("demo of an unsupported file".to_string()), + ], + ), + ]; + Ok(EntriesToCompare( + two_sides_map + .into_iter() + .map(|(key, [left, right])| (PathBuf::from(key), [left, right.clone(), right])) + .collect(), + )) + } + + fn save_unchecked( + &mut self, + result: indexmap::IndexMap, + ) -> Result<(), DataSaveError> { + eprintln!("Can't save fake demo data. Here it is as TOML"); + eprintln!(); + eprintln!( + "{}", + toml::to_string(&result).unwrap_or_else(|err| format!("Failed to parse TOML: {err}")) + ); + Err(DataSaveError::CannotSaveFakeData) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn it_works() { + insta::assert_yaml_snapshot!(FakeData.scan().unwrap(), + @r###" + --- + added file: + - ~ + - added + - added + deleted_file: + - deleted + - ~ + - ~ + edited_file: + - "First\nThird\nFourth\nFourthAndAHalf\n\nFifth\nSixth\n----\none two" + - "First\nSecond\nThird\nFifth\nSixth\n----\none\n" + - "First\nSecond\nThird\nFifth\nSixth\n----\none\n" + "###); + } +} diff --git a/backend-tauri/src/main.rs b/backend-tauri/src/main.rs index 93cfcdc..f484750 100644 --- a/backend-tauri/src/main.rs +++ b/backend-tauri/src/main.rs @@ -4,7 +4,7 @@ // #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] use clap::Parser; -use diffedit3::{DataInterface, ThreeDirInput}; +use diffedit3::DataInterface; use indexmap::IndexMap; // Using parking_lot::Mutex for a timeout. We could alternatively use // tokio::sync::Mutex, but the docs suggest only using it if absolutely @@ -12,7 +12,7 @@ use indexmap::IndexMap; use parking_lot::Mutex; use tauri::{CustomMenuItem, Menu, Submenu}; -type DataMutex = Mutex; +type DataMutex = Mutex>; #[tauri::command] fn args() -> Vec { @@ -53,10 +53,11 @@ fn get_merge_data( // CSS property fn main() { let cli = diffedit3::Cli::parse(); - let input: diffedit3::ThreeDirInput = cli.try_into().unwrap_or_else(|err| { - eprintln!("Error: {err}"); - std::process::exit(2) - }); + let input: Box = + cli.into_data_interface().unwrap_or_else(|err| { + eprintln!("Error: {err}"); + std::process::exit(2) + }); let input_mutex: DataMutex = Mutex::new(input); let abandon_changes_and_quit = CustomMenuItem::new(