use std::path::Path; use crate::consts::*; pub fn build_folder_layout(root: impl AsRef) -> std::io::Result<()> { std::fs::create_dir_all( root.as_ref() .join(SETTING_FOLDER) .join(ID_FOLDER) )?; std::fs::create_dir_all( root.as_ref() .join(SETTING_FOLDER) .join(APP_ID_FOLDER) )?; std::fs::create_dir_all( root.as_ref() .join(SETTING_FOLDER) .join(USER_ID_FOLDER) )?; std::fs::create_dir_all( root.as_ref() .join(SETTING_FOLDER) .join(TAG_FOLDER) )?; Ok(()) } pub fn rebuild_symlinks(root: impl AsRef) -> std::io::Result<()> { for dir_entry in root.as_ref() .join(SETTING_FOLDER) .join(ID_FOLDER) .read_dir()? { let dir_entry = dir_entry?; if dir_entry.file_type()?.is_file() { let f_path = dir_entry.path(); if let Some(ext) = f_path.extension() { if ext == RON_EXTENSION { let reader = std::io::BufReader::new(std::fs::File::open(&f_path)?); let setting: community_settings_core::v1::Metadata = match ron::de::from_reader(reader) { Ok(x) => x, Err(e) => { log::debug!("Error while reading {}: {}", f_path.display(), e); let e_msg = format!("{}", e); return Err(std::io::Error::new(std::io::ErrorKind::InvalidData, e_msg)); } }; let to_symlink = crate::file_util::symlinks(&root, &setting)?; let path_ron_canon = f_path.canonicalize()?; let path_json_canon = crate::file_util::setting_path_by_id(&root, setting.get_id(), JSON_EXTENSION).canonicalize()?; for ron_link in to_symlink.ron { if ron_link.exists() { continue; } log::info!("Rebuilding symlink {} -> {}", ron_link.display(), path_ron_canon.display()); #[cfg(target_family = "windows")] // NOTE: windows support is untested and unmaintained { std::os::windows::fs::symlink_file(&path_ron_canon, &ron_link)?; } #[cfg(target_family = "unix")] { std::os::unix::fs::symlink(&path_ron_canon, &ron_link)?; } } for json_link in to_symlink.json { if json_link.exists() { continue; } log::info!("Rebuilding symlink {} -> {}", json_link.display(), path_json_canon.display()); #[cfg(target_family = "windows")] // NOTE: windows support is untested and unmaintained { std::os::windows::fs::symlink_file(&path_json_canon, json_link)?; } #[cfg(target_family = "unix")] { std::os::unix::fs::symlink(&path_json_canon, json_link)?; } } } } } } Ok(()) } pub fn fix_symlinks(root: impl AsRef) -> std::io::Result<()> { log::info!("root setttings folder: {} aka {} (absolute)", root.as_ref().display(), root.as_ref().canonicalize()?.display()); for dir_entry in root.as_ref() .join(SETTING_FOLDER) .join(APP_ID_FOLDER) .read_dir()? { let dir_entry = dir_entry?; if dir_entry.file_type()?.is_dir() { make_symlinks_absolute_in_dir(root.as_ref(), dir_entry.path())?; } } for dir_entry in root.as_ref() .join(SETTING_FOLDER) .join(USER_ID_FOLDER) .read_dir()? { let dir_entry = dir_entry?; if dir_entry.file_type()?.is_dir() { make_symlinks_absolute_in_dir(root.as_ref(), dir_entry.path())?; } } for dir_entry in root.as_ref() .join(SETTING_FOLDER) .join(TAG_FOLDER).read_dir()? { let dir_entry = dir_entry?; if dir_entry.file_type()?.is_dir() { make_symlinks_absolute_in_dir(root.as_ref(), dir_entry.path())?; } } Ok(()) } fn make_symlinks_absolute_in_dir(root: impl AsRef, dir: impl AsRef) -> std::io::Result<()> { let abs_root = root.as_ref().canonicalize()?; assert!(abs_root.is_absolute()); for dir_entry in dir.as_ref() .read_dir()? { let dir_entry = dir_entry?; if dir_entry.file_type()?.is_symlink() { let path = dir_entry.path(); let link_path = path.read_link()?; if !link_path.is_absolute() { let new_link = abs_root.join( link_path.strip_prefix(&root).expect("Symlinked path does not begin with root settings folder") ); log::info!("Fixing {} -> {} to -> {}", path.display(), link_path.display(), new_link.display()); std::fs::remove_file(&path)?; #[cfg(target_family = "windows")] // NOTE: windows support is untested and unmaintained { std::os::windows::fs::symlink_file(new_link, &path)?; } #[cfg(target_family = "unix")] { std::os::unix::fs::symlink(new_link, &path)?; } }else { log::info!("Found already-absolute symlink {} -> {}", path.display(), link_path.display()); } } else { log::info!("Found non-symlink {}: {:?}", dir_entry.path().display(), dir_entry.file_type()?); } } Ok(()) } pub fn sync_ids(root: impl AsRef) -> std::io::Result<()> { for dir_entry in root.as_ref() .join(SETTING_FOLDER) .join(ID_FOLDER) .read_dir()? { let dir_entry = dir_entry?; let f_path = dir_entry.path(); if let Some(ext) = f_path.extension() { let id = f_path.file_stem().map(|os| os.to_string_lossy().to_string()).unwrap(); if ext == RON_EXTENSION { let reader = std::io::BufReader::new(std::fs::File::open(&f_path)?); let mut setting: community_settings_core::v1::Metadata = match ron::de::from_reader(reader) { Ok(x) => x, Err(e) => { log::debug!("Error while reading {}: {}", f_path.display(), e); let e_msg = format!("{}", e); return Err(std::io::Error::new(std::io::ErrorKind::InvalidData, e_msg)); } }; if setting.id != id { setting.id = id; ron::ser::to_writer(std::fs::File::create(&f_path)?, &setting).unwrap(); } } else if ext == JSON_EXTENSION { let reader = std::io::BufReader::new(std::fs::File::open(&f_path)?); let mut setting: community_settings_core::v1::Metadata = match serde_json::from_reader(reader) { Ok(x) => x, Err(e) => { log::debug!("Error while reading {}: {}", f_path.display(), e); let e_msg = format!("{}", e); return Err(std::io::Error::new(std::io::ErrorKind::InvalidData, e_msg)); } }; if setting.id != id { setting.id = id; serde_json::to_writer(std::fs::File::create(&f_path)?, &setting).unwrap(); } } } } Ok(()) } pub fn make_tag_subfolders(root: impl AsRef) -> std::io::Result<()> { for dir_entry in root.as_ref() .join(SETTING_FOLDER) .join(USER_ID_FOLDER) .read_dir()? { let dir_entry = dir_entry?; if dir_entry.metadata()?.is_dir() { let tag_folder = dir_entry.path().join(TAG_FOLDER); if !tag_folder.exists() { std::fs::create_dir(&tag_folder)?; } } } for dir_entry in root.as_ref() .join(SETTING_FOLDER) .join(APP_ID_FOLDER) .read_dir()? { let dir_entry = dir_entry?; if dir_entry.metadata()?.is_dir() { let tag_folder = dir_entry.path().join(TAG_FOLDER); if !tag_folder.exists() { std::fs::create_dir(&tag_folder)?; } } } // TODO populate folders Ok(()) }