<commit_before>use crate::checksum;
use crate::config;
use crate::errors::DotcopterError;
use crate::model::*;
use dirs;
use slog::Logger;
use slog::{debug, error, info, o, warn};
use std;
use std::error::Error;
use std::fs;
use std::path::Path;
use yaml_rust::Yaml;

pub fn process_dot_files(log: &Logger, dot_files: &Yaml, force: bool) {
  if dot_files.is_badvalue() {
    warn!(log, "Empty files list");
  } else {
    for dot_file in config::parse_dot_files(log, dot_files) {
      process_dot_file(log, &dot_file, force);
    }
  }
}

fn process_dot_file(log: &Logger, dot_file: &DotFile, force: bool) {
  let log = &log.new(o!("target" => dot_file.target.clone(), "source" => dot_file.source.clone(), "type" => format!("{:?}", dot_file.dot_file_type)));
  debug!(log, "Process entry");
  let source_with_resolved_home = resolve_home(log, &dot_file.source);
  let target_with_resolved_home = resolve_home(log, &dot_file.target);
  let source_path = Path::new(&source_with_resolved_home);
  let target_path = Path::new(&target_with_resolved_home);
  if !source_path.exists() {
    warn!(log, "Source path does not exist");
    return;
  }
  match dot_file.dot_file_type {
    DotFileType::LINK => process_link(log, source_path, target_path, force),
    DotFileType::COPY => process_copy(log, source_path, target_path, force),
  }
}

fn resolve_home(log: &Logger, path: &str) -> String {
  if let Some(home_dir) = dirs::home_dir() {
    if path.starts_with('~') {
      let mut home_string = home_dir.into_os_string().into_string().expect("home_dir should be a valid string");
      home_string.push_str(&path[1..]);
      return home_string;
    } else {
      return path.to_string();
    }
  } else {
    warn!(log, "Home dir not set");
    return path.to_string();
  }
}

fn process_copy(log: &Logger, source_path: &Path, target_path: &Path, force: bool) {
  match has_same_content(log, source_path, target_path) {
    Ok(true) => info!(log, "File already there"),
    Ok(false) => {
      if !force && target_path.exists() {
        error!(log, "Target already exists but has different content.");
        return;
      }
      match copy_dot_file(source_path, target_path) {
        Ok(_) => info!(log, "Copied file successfully"),
        Err(e) => error!(log, "Failed to copy file"; "error" => e.description()),
      }
    }
    Err(e) => error!(log, "Failed to copy dotfile"; "error" => e.description()),
  }
}

fn copy_dot_file(source: &Path, target: &Path) -> Result<(), DotcopterError> {
  if let Some(parent) = target.parent() {
    fs::create_dir_all(parent)?;<commit_msg>[bump] rust 1.38.0<commit_after>use crate::checksum;
use crate::config;
use crate::errors::DotcopterError;
use crate::model::*;
use dirs;
use slog::Logger;
use slog::{debug, error, info, o, warn};
use std;
use std::error::Error;
use std::fs;
use std::path::Path;
use yaml_rust::Yaml;

pub fn process_dot_files(log: &Logger, dot_files: &Yaml, force: bool) {
  if dot_files.is_badvalue() {
    warn!(log, "Empty files list");
  } else {
    for dot_file in config::parse_dot_files(log, dot_files) {
      process_dot_file(log, &dot_file, force);
    }
  }
}

fn process_dot_file(log: &Logger, dot_file: &DotFile, force: bool) {
  let log = &log.new(o!("target" => dot_file.target.clone(), "source" => dot_file.source.clone(), "type" => format!("{:?}", dot_file.dot_file_type)));
  debug!(log, "Process entry");
  let source_with_resolved_home = resolve_home(log, &dot_file.source);
  let target_with_resolved_home = resolve_home(log, &dot_file.target);
  let source_path = Path::new(&source_with_resolved_home);
  let target_path = Path::new(&target_with_resolved_home);
  if !source_path.exists() {
    warn!(log, "Source path does not exist");
    return;
  }
  match dot_file.dot_file_type {
    DotFileType::LINK => process_link(log, source_path, target_path, force),
    DotFileType::COPY => process_copy(log, source_path, target_path, force),
  }
}

fn resolve_home(log: &Logger, path: &str) -> String {
  if let Some(home_dir) = dirs::home_dir() {
    if path.starts_with('~') {
      let mut home_string = home_dir.into_os_string().into_string().expect("home_dir should be a valid string");
      home_string.push_str(&path[1..]);
      home_string
    } else {
      path.to_string()
    }
  } else {
    warn!(log, "Home dir not set");
    path.to_string()
  }
}

fn process_copy(log: &Logger, source_path: &Path, target_path: &Path, force: bool) {
  match has_same_content(log, source_path, target_path) {
    Ok(true) => info!(log, "File already there"),
    Ok(false) => {
      if !force && target_path.exists() {
        error!(log, "Target already exists but has different content.");
        return;
      }
      match copy_dot_file(source_path, target_path) {
        Ok(_) => info!(log, "Copied file successfully"),
        Err(e) => error!(log, "Failed to copy file"; "error" => e.description()),
      }
    }
    Err(e) => error!(log, "Failed to copy dotfile"; "error" => e.description()),
  }
}

fn copy_dot_file(source: &Path, target: &Path) -> Result<(), DotcopterError> {
  if let Some(parent) = target.parent() {
    fs::create_dir_all(parent)?;
{'fraction_non_alphanumeric': 0.16256539430342956, 'fraction_numerical': 0.0011625653943034295, 'mean_word_length': 4.2673469387755105, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 33, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1297637', 'n_tokens_mistral': 1808, 'n_tokens_neox': 1793, 'n_words': 412}