 cf58c5617a
			
		
	
	cf58c5617a
	
	
	
		
			
			core: Tidy contributor onboarding. - Fixes typos. - Fixes stale links. - Tidies Makefile so that Poetry env is optional for hygiene commands. - Remove mismatched YAML naming. - Uses shebang on Python scripts. - Document semver usage. - Redirect OpenAPI schema. Signed-off-by: Teffen Ellis <592134+GirlBossRush@users.noreply.github.com>
		
			
				
	
	
		
			442 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
			
		
		
	
	
			442 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
| use std::{
 | |
|     collections::{HashMap, HashSet, VecDeque}, env::consts::OS, ffi::OsStr, fs::{create_dir_all, read_to_string, remove_file, write, File}, path::{Component, PathBuf}, process::Command
 | |
| };
 | |
| 
 | |
| use colored::Colorize;
 | |
| use regex::{Captures, Regex};
 | |
| 
 | |
| use crate::{hackyfixes::add_extra_dot_dot_to_expression_mdx, migratefile::read_migrate_file, recurse_directory, Cli};
 | |
| 
 | |
| pub fn migrate(quiet: bool, migratefile: PathBuf, migrate_path: PathBuf) {
 | |
|     if !quiet {
 | |
|         println!("Reading migrate file");
 | |
|     }
 | |
|     let files = read_migrate_file(migratefile);
 | |
| 
 | |
|     let files = match files {
 | |
|         Ok(i) => {
 | |
|             if !quiet {
 | |
|                 println!("{}", "Success".green());
 | |
|             }
 | |
|             i
 | |
|         }
 | |
|         Err(_) => {
 | |
|             println!("{}: Could not read migrate file", "Error".red());
 | |
|             return;
 | |
|         }
 | |
|     };
 | |
| 
 | |
|     replace_links(migrate_path.clone(), files.clone());
 | |
|     let successful_moves = move_files(quiet, migrate_path.clone(), files);
 | |
|     add_redirects(successful_moves.clone(), migrate_path.clone());
 | |
|     //shorten_all_external_links(migrate_path);
 | |
|     add_extra_dot_dot_to_expression_mdx(migrate_path.clone());
 | |
|     let _ = Command::new("sh")
 | |
|         .arg("-c")
 | |
|         .arg("find . -empty -type d -delete")
 | |
|         .current_dir(migrate_path)
 | |
|         .output();
 | |
| }
 | |
| 
 | |
| pub fn unmigrate(quiet: bool, migratefile: PathBuf, migrate_path: PathBuf) {
 | |
|     if !quiet {
 | |
|         println!("Reading migrate file");
 | |
|     }
 | |
|     let files = read_migrate_file(migratefile);
 | |
| 
 | |
|     let files = match files {
 | |
|         Ok(i) => {
 | |
|             if !quiet {
 | |
|                 println!("{}", "Success".green());
 | |
|             }
 | |
|             i
 | |
|         }
 | |
|         Err(_) => {
 | |
|             println!("{}: Could not read migrate file", "Error".red());
 | |
|             return;
 | |
|         }
 | |
|     };
 | |
| 
 | |
|     let files: Vec<(PathBuf, PathBuf)> = files.iter().map(|x| (x.1.clone(), x.0.clone())).collect(); //switch files to reverse a migration
 | |
|     replace_links(migrate_path.clone(), files.clone());
 | |
|     let successful_moves = move_files(quiet, migrate_path.clone(), files);
 | |
|     let successful_moves: Vec<(PathBuf, PathBuf)> = successful_moves
 | |
|         .iter()
 | |
|         .map(|x| (x.1.clone(), x.0.clone()))
 | |
|         .collect(); //switch files to reverse a migration
 | |
|     remove_redirects(successful_moves, migrate_path.clone());
 | |
|     //shorten_all_external_links(migrate_path);
 | |
| }
 | |
| 
 | |
| fn move_files(
 | |
|     quiet: bool,
 | |
|     migrate_path: PathBuf,
 | |
|     files: Vec<(PathBuf, PathBuf)>,
 | |
| ) -> Vec<(PathBuf, PathBuf)> {
 | |
|     let mut successful_moves = vec![];
 | |
|     for file in files {
 | |
|         if !quiet {
 | |
|             print!("{} -> {} : ", file.0.display(), file.1.display());
 | |
|         }
 | |
|         let rename: anyhow::Result<()> = (|| {
 | |
|             let old_file = migrate_path.join(&file.0);
 | |
|             let new_file = migrate_path.join(&file.1);
 | |
|             std::fs::create_dir_all(&new_file.parent().expect("files to have a parent"))?;
 | |
|             std::fs::rename(&old_file, &new_file)?;
 | |
|             Ok(())
 | |
|         })();
 | |
|         match rename {
 | |
|             Ok(_) => {
 | |
|                 if !quiet {
 | |
|                     println!("{}", "Success".green());
 | |
|                 }
 | |
|                 successful_moves.push(file);
 | |
|             }
 | |
|             Err(_) => println!(
 | |
|                 "{}: Could not move file {}",
 | |
|                 "Error".red(),
 | |
|                 file.0.display()
 | |
|             ),
 | |
|         };
 | |
|     }
 | |
|     successful_moves
 | |
| }
 | |
| 
 | |
| fn replace_links(migrate_path: PathBuf, moves: Vec<(PathBuf, PathBuf)>) {
 | |
|     let files = recurse_directory(migrate_path.clone());
 | |
|     let mut moved = HashSet::new();
 | |
| 
 | |
|     let mut absolute_moves = vec![];
 | |
|     for r#move in &moves {
 | |
|         let r#move = (
 | |
|             migrate_path.join(r#move.0.clone()),
 | |
|             migrate_path.join(r#move.1.clone()),
 | |
|         );
 | |
|         let absolute_move_0 = r#move
 | |
|             .0
 | |
|             .canonicalize()
 | |
|             .expect(&format!("{}", r#move.0.display()));
 | |
| 
 | |
|         let _ = create_dir_all(r#move.1.parent().unwrap());
 | |
|         let tmp_file = File::create_new(&r#move.1);
 | |
|         let absolute_move_1 = r#move.1.clone().canonicalize().expect(&format!(
 | |
|             "{} {:?}",
 | |
|             r#move.1.display(),
 | |
|             tmp_file
 | |
|         ));
 | |
|         // delete file if it didn't already exist
 | |
|         if let Ok(_) = tmp_file {
 | |
|             let _ = remove_file(&r#move.1);
 | |
|         };
 | |
|         absolute_moves.push((absolute_move_0, absolute_move_1));
 | |
|     }
 | |
|     let absolute_moves = absolute_moves
 | |
|         .iter()
 | |
|         .map(|x| x.clone())
 | |
|         .collect::<HashMap<PathBuf, PathBuf>>();
 | |
| 
 | |
|     for file in files {
 | |
|         let absolute_file = file.canonicalize().unwrap();
 | |
|         println!("{}", absolute_file.display());
 | |
|         let mut contents = match read_to_string(file.clone()) {
 | |
|             Ok(i) => i,
 | |
|             Err(_) => continue,
 | |
|         };
 | |
| 
 | |
|         // replace old absolute file with the new absolute file
 | |
|         let old_absolute_file = absolute_file.clone();
 | |
|         let absolute_file = match absolute_moves.get(&absolute_file) {
 | |
|             Some(file) => {
 | |
|                 println!("    new file: {}", file.display());
 | |
|                 moved.insert(absolute_file);
 | |
|                 file.clone()
 | |
|             }
 | |
|             None => absolute_file.clone(),
 | |
|         };
 | |
| 
 | |
|         // get all links in file and remove web links and link to self
 | |
|         let re = Regex::new(r"\[(?<name>[\w \-\*'`]*)\]\((?<link>[\w\-\\/\\.#]*)\)").unwrap();
 | |
|         let tmp_contents = contents.clone();
 | |
|         let captures: Vec<Captures> = re
 | |
|             .captures_iter(&tmp_contents)
 | |
|             .filter(|x| {
 | |
|                 let link = &x["link"];
 | |
| 
 | |
|                 !["http", "#", "/"]
 | |
|                     .iter()
 | |
|                     .fold(false, |acc, x| acc || link.starts_with(x))
 | |
|             })
 | |
|             .collect();
 | |
|         println!("    captures: {}\n", captures.len());
 | |
| 
 | |
|         for capture in captures {
 | |
|             let mut capture_log = String::new();
 | |
|             let link = capture["link"].to_owned();
 | |
|             let link_path;
 | |
| 
 | |
|             let link_postfix_index = link.find('#');
 | |
| 
 | |
|             let link_postfix = match link_postfix_index {
 | |
|                 Some(i) => {
 | |
|                     let link_postfix = link[i..].to_owned();
 | |
|                     link_path = link[..i].to_owned();
 | |
|                     Some(link_postfix)
 | |
|                 }
 | |
|                 None => {
 | |
|                     link_path = link.clone();
 | |
|                     None
 | |
|                 },
 | |
|             };
 | |
| 
 | |
|             let absolute_link = old_absolute_file.parent().unwrap().join(link_path.clone());
 | |
|             //let _ = create_dir_all(absolute_link.parent().unwrap());
 | |
|             //let tmp_file = File::create_new(&absolute_link);
 | |
| 
 | |
|             let absolute_link = match absolute_link
 | |
|                 .canonicalize()
 | |
|                 .or(absolute_link.with_extension("md").canonicalize())
 | |
|                 .or(absolute_link.with_extension("mdx").canonicalize())
 | |
|             {
 | |
|                 Ok(link) => link,
 | |
|                 _ => {
 | |
|                     println!(
 | |
|                         "    {}: {} -> {}",
 | |
|                         "failed".red(),
 | |
|                         absolute_file.to_string_lossy().to_string().red(),
 | |
|                         absolute_link.to_string_lossy().to_string().red()
 | |
|                     );
 | |
|                     continue;
 | |
|                 }
 | |
|             };
 | |
|             let absolute_link = if absolute_link.is_file() {
 | |
|                 absolute_link
 | |
|             } else if absolute_link.join("index.md").is_file() {
 | |
|                 absolute_link.join("index.md")
 | |
|             } else if absolute_link.join("index.mdx").is_file() {
 | |
|                 absolute_link.join("index.mdx")
 | |
|             } else {
 | |
|                 println!(
 | |
|                     "    {}: {} -> {}",
 | |
|                     "failed".red(),
 | |
|                     absolute_file.to_string_lossy().to_string().red(),
 | |
|                     absolute_link.to_string_lossy().to_string().red()
 | |
|                 );
 | |
|                 continue;
 | |
|             };
 | |
|             // delete file if it didn't already exist
 | |
|             //if let Ok(_) = tmp_file {
 | |
|             //    let _ = remove_file(&absolute_link);
 | |
|             //};
 | |
|             capture_log.push_str(&format!("    oldalink: {}\n", absolute_link.display()));
 | |
| 
 | |
|             // replace old absolute link with the new absolute link
 | |
|             let absolute_link = match absolute_moves.get(&absolute_link) {
 | |
|                 Some(link) => link.clone(),
 | |
|                 None => absolute_link.clone(),
 | |
|             };
 | |
| 
 | |
|             capture_log.push_str(&format!("    newalink: {}\n", absolute_link.display()));
 | |
| 
 | |
|             // create tmp absolutes and make them into components
 | |
|             let tmp_absolute_file = absolute_file.clone();
 | |
|             let mut tmp_absolute_file = tmp_absolute_file.components().collect::<VecDeque<_>>();
 | |
|             let tmp_absolute_link = absolute_link.clone();
 | |
|             let mut tmp_absolute_link = tmp_absolute_link.components().collect::<VecDeque<_>>();
 | |
|             // remove the shared path components
 | |
|             loop {
 | |
|                 if tmp_absolute_file.front() != tmp_absolute_link.front()
 | |
|                     || tmp_absolute_file.front() == None
 | |
|                 {
 | |
|                     break;
 | |
|                 }
 | |
|                 tmp_absolute_file.pop_front();
 | |
|                 tmp_absolute_link.pop_front();
 | |
|             }
 | |
|             capture_log.push_str(&format!(
 | |
|                 "    shrtfile: {}\n",
 | |
|                 tmp_absolute_file.iter().collect::<PathBuf>().display()
 | |
|             ));
 | |
|             capture_log.push_str(&format!(
 | |
|                 "    shrtlink: {}\n",
 | |
|                 tmp_absolute_link.iter().collect::<PathBuf>().display()
 | |
|             ));
 | |
| 
 | |
|             if tmp_absolute_file.len() <= 0 {
 | |
|                 println!(
 | |
|                     "    {}: {} -> {}",
 | |
|                     "failed".red(),
 | |
|                     absolute_file.to_string_lossy().to_string().red(),
 | |
|                     absolute_link.to_string_lossy().to_string().red()
 | |
|                 );
 | |
|                 continue;
 | |
|             }
 | |
|             let escapes = (0..tmp_absolute_file.len() - 1)
 | |
|                 .map(|_| Component::Normal("..".as_ref()))
 | |
|                 .collect::<PathBuf>();
 | |
| 
 | |
|             let new_link = escapes.join(tmp_absolute_link.iter().collect::<PathBuf>());
 | |
|             // add a . to the beginning if it doesn't already start with . or ..
 | |
|             let new_link = match new_link
 | |
|                 .components()
 | |
|                 .collect::<Vec<_>>()
 | |
|                 .first()
 | |
|                 .iter()
 | |
|                 .collect::<PathBuf>()
 | |
|                 .to_str()
 | |
|             {
 | |
|                 Some(".") => new_link,
 | |
|                 Some("..") => new_link,
 | |
|                 _ => PathBuf::from(".").join(new_link),
 | |
|             };
 | |
|             let mut new_link = new_link.to_string_lossy().to_string();
 | |
|             match link_postfix {
 | |
|                 Some(i) => new_link.push_str(&i),
 | |
|                 None => {}
 | |
|             }
 | |
|             capture_log.push_str(&format!("    old link: {}\n", link));
 | |
|             capture_log.push_str(&format!("    new link: {}\n", new_link));
 | |
|             print!("{}", capture_log);
 | |
|             //println!("{} {} {}", absolute_file.display(), absolute_link.display(), new_link.display());
 | |
|             let tmp_contents = contents.replace(&format!("({})", link), &format!("({})", new_link));
 | |
|             if tmp_contents == contents {
 | |
|                 println!("{}", "    nothing replaced".yellow());
 | |
|             } else {
 | |
|                 contents = tmp_contents;
 | |
|             };
 | |
|             println!("");
 | |
|         }
 | |
| 
 | |
|         write(file, contents).unwrap();
 | |
|     }
 | |
| }
 | |
| 
 | |
| fn fix_internal_links_in_file(migrate_path: PathBuf, move_from: PathBuf, move_to: PathBuf) {
 | |
|     let move_from = migrate_path.join(move_from);
 | |
|     let move_to = migrate_path.join(move_to);
 | |
|     let contents = read_to_string(&move_from);
 | |
|     let mut contents = match contents {
 | |
|         Ok(ok) => ok,
 | |
|         Err(_) => return,
 | |
|     };
 | |
|     let re = Regex::new(r"\[(?<name>.*)\]\((?<link>.*)\)").unwrap();
 | |
|     let captures: Vec<Captures> = re.captures_iter(&contents).collect();
 | |
|     let mut changes = vec![];
 | |
|     for capture in captures {
 | |
|         //let name = &capture["name"];
 | |
|         let link = &capture["link"];
 | |
|         if link.starts_with('#') || link.starts_with("http") {
 | |
|             continue;
 | |
|         }
 | |
|         let link = PathBuf::from(link);
 | |
|         //println!("{} {}", move_from.display(), link.display());
 | |
|         let absolute_link = move_from
 | |
|             .parent()
 | |
|             .unwrap()
 | |
|             .canonicalize()
 | |
|             .unwrap()
 | |
|             .join(&link);
 | |
|         if move_to.components().collect::<Vec<_>>().len() > 1 {
 | |
|             let _ = create_dir_all(move_to.parent().unwrap());
 | |
|         }
 | |
|         let tmp_file = File::create_new(move_to.clone());
 | |
|         //println!("{} {} {} {}", name, link.display(), absolute_link.display(), make_path_relative(absolute_link.clone(), move_to.canonicalize().unwrap().clone()).display());
 | |
|         let new_link = make_path_relative(
 | |
|             absolute_link.clone(),
 | |
|             move_to.canonicalize().unwrap().clone(),
 | |
|         );
 | |
|         if let Ok(_) = tmp_file {
 | |
|             remove_file(move_to.clone()).unwrap()
 | |
|         };
 | |
|         changes.push((link.clone(), new_link.clone()));
 | |
|     }
 | |
|     for i in changes {
 | |
|         contents = contents.replace(
 | |
|             &format!("({})", i.0.display()),
 | |
|             &format!("({})", i.1.display()),
 | |
|         );
 | |
|     }
 | |
|     write(move_from, contents).unwrap();
 | |
| }
 | |
| 
 | |
| fn make_path_relative(path: PathBuf, relative_to: PathBuf) -> PathBuf {
 | |
|     let mut subdirs = 0;
 | |
|     let path_components = path.components().collect::<Vec<_>>();
 | |
|     let relative_to_components = relative_to.components().collect::<Vec<_>>();
 | |
|     loop {
 | |
|         if path_components.len() <= subdirs {
 | |
|             break;
 | |
|         } else if path_components[subdirs] != relative_to_components[subdirs] {
 | |
|             break;
 | |
|         }
 | |
|         subdirs += 1;
 | |
|     }
 | |
|     let new_path = &path_components[subdirs..].iter().collect::<PathBuf>();
 | |
|     let backouts = (0..relative_to_components.len() - subdirs - 1)
 | |
|         .map(|_| PathBuf::from(".."))
 | |
|         .reduce(|acc, e| acc.join(e))
 | |
|         .unwrap_or(PathBuf::from(""));
 | |
|     //println!("{}, {}", relative_to_components.len() - subdirs - 1, backouts.display());
 | |
|     let new_path = backouts.join(new_path);
 | |
|     let new_path = if new_path
 | |
|         .to_string_lossy()
 | |
|         .to_string()
 | |
|         .chars()
 | |
|         .next()
 | |
|         .unwrap()
 | |
|         != '.'
 | |
|     {
 | |
|         PathBuf::from(".").join(new_path)
 | |
|     } else {
 | |
|         new_path
 | |
|     };
 | |
| 
 | |
|     let new_path = if new_path.file_name() == Some(OsStr::new("index.md"))
 | |
|         || new_path.file_name() == Some(OsStr::new("index.mdx"))
 | |
|     {
 | |
|         new_path.parent().unwrap().to_path_buf()
 | |
|     } else {
 | |
|         new_path
 | |
|     };
 | |
| 
 | |
|     new_path
 | |
| }
 | |
| 
 | |
| fn add_redirects(successful_moves: Vec<(PathBuf, PathBuf)>, migrate_path: PathBuf) {
 | |
|     let redirects = generate_redirects(successful_moves);
 | |
|     let netlify_path = migrate_path.parent().unwrap().join("netlify.toml");
 | |
|     let mut netlify_contents = read_to_string(netlify_path.clone()).unwrap();
 | |
|     for redirect in redirects {
 | |
|         netlify_contents.push_str(&redirect);
 | |
|     }
 | |
|     std::fs::write(netlify_path, netlify_contents).unwrap();
 | |
| }
 | |
| 
 | |
| fn remove_redirects(successful_moves: Vec<(PathBuf, PathBuf)>, migrate_path: PathBuf) {
 | |
|     let redirects = generate_redirects(successful_moves);
 | |
|     let netlify_path = migrate_path.parent().unwrap().join("netlify.toml");
 | |
|     let mut netlify_contents = read_to_string(netlify_path.clone()).unwrap();
 | |
|     for redirect in redirects {
 | |
|         netlify_contents = netlify_contents.replace(&redirect, "");
 | |
|     }
 | |
|     std::fs::write(netlify_path, netlify_contents).unwrap();
 | |
| }
 | |
| 
 | |
| fn generate_redirects(successful_moves: Vec<(PathBuf, PathBuf)>) -> Vec<String> {
 | |
|     successful_moves
 | |
|         .iter()
 | |
|         .map(|x| {
 | |
|             format!(
 | |
|                 "
 | |
| [[redirects]]
 | |
|   from = \"{}\"
 | |
|   to = \"{}\"
 | |
|   status = 301
 | |
|   force = true
 | |
| ",
 | |
|                 x.0.display(),
 | |
|                 x.1.display()
 | |
|             )
 | |
|         })
 | |
|         .collect()
 | |
| }
 |