use std::hash::{self, Hash}; use std::path::{Path, PathBuf}; use std::sync::{Arc, Mutex}; use crate::{Error, Result}; use java::{JAVA_EXT_CLASS, JAVA_EXT_SOURCE}; use path::PathHandler; use serde::{Deserialize, Serialize}; #[derive(Debug, Clone, Deserialize, Serialize)] pub struct Class { #[serde(skip_deserializing, skip_serializing)] ph: Option>>, path: PathBuf, checksum: String, } impl Class { pub fn new>(ph: Arc>, path: P, checksum: String) -> Self { Self { ph: Some(ph), path: path.as_ref().to_path_buf(), checksum, } } pub fn from_file>(ph: Arc>, path: P) -> Result { let mut path = path.as_ref().to_path_buf(); if path.is_relative() { path = path.canonicalize()?; } let dir_src = ph.try_lock()?.dir_src(); Ok(Self { ph: Some(ph), path: PathBuf::from( pathsub::sub_paths(path.as_path(), dir_src.as_path()).ok_or(Error::PathSub)?, ) .with_extension(""), checksum: sha256::try_digest(&path)?, }) } pub fn path(&self) -> PathBuf { self.path.clone() } pub fn checksum(&self) -> String { self.checksum.clone() } /// Returns the path such that src/ and target/ don't matter. /// E.g., `main/Main.java` as_subpath is `Main.java` /// allowing it to match with `target/main/Main.class` /// because Java diregards the top level subdir in `src/` fn subpath(&self) -> PathBuf { let mut p = self.path.components(); p.next(); // Remove the top level dir. p.as_path().to_path_buf() } /// Returns true if the class needs updating. /// This may also cautionarily return true if it cannot digest the file. pub fn is_updated(&mut self) -> Result { // Still doesn't handle inter-dependency checks. // Hopefully then I'll clean this horror up. let mut ph = self .ph .as_ref() .ok_or(Error::MissingPathHandler)? .try_lock()?; Ok(ph .dir_target() .join(self.subpath()) .with_extension(JAVA_EXT_CLASS) .exists() && sha256::try_digest( ph.dir_src() .join(self.path()) .with_extension(JAVA_EXT_SOURCE), ) .is_ok_and(|hash| self.checksum == hash)) } pub fn set_path_handler(&mut self, ph: Arc>) { self.ph = Some(ph); } pub fn with_path_handler(&mut self, ph: Arc>) -> &mut Self { self.ph = Some(ph); self } } impl Hash for Class { fn hash(&self, state: &mut H) { self.path.hash(state); } } impl PartialEq for Class { fn eq(&self, other: &Self) -> bool { self.path == other.path } fn ne(&self, other: &Self) -> bool { self.path != other.path } } impl Eq for Class {}