diff --git a/src/ilias.rs b/src/ilias.rs index eaad6a0..48761dd 100644 --- a/src/ilias.rs +++ b/src/ilias.rs @@ -4,14 +4,13 @@ use std::{collections::HashMap, error::Error as _, io::Write, sync::Arc}; use anyhow::{anyhow, Context, Result}; use cookie_store::CookieStore; -use ignore::gitignore::Gitignore; use once_cell::sync::Lazy; use reqwest::{Client, IntoUrl, Proxy, Url}; use reqwest_cookie_store::CookieStoreMutex; use scraper::{ElementRef, Html, Selector}; use serde_json::json; -use crate::{cli::Opt, queue, util::wrap_html, ILIAS_URL}; +use crate::{cli::Opt, queue, util::wrap_html, ILIAS_URL, iliasignore::IliasIgnore}; pub mod course; pub mod exercise; @@ -35,7 +34,7 @@ static CONTAINER_ITEM_TITLE: Lazy = pub struct ILIAS { pub opt: Opt, - pub ignore: Gitignore, + pub ignore: IliasIgnore, client: Client, cookies: Arc, pub course_names: HashMap, @@ -61,7 +60,7 @@ impl ILIAS { pub async fn with_session( opt: Opt, session: Arc, - ignore: Gitignore, + ignore: IliasIgnore, course_names: HashMap, ) -> Result { let mut builder = Client::builder() @@ -88,7 +87,7 @@ impl ILIAS { opt: Opt, user: &str, pass: &str, - ignore: Gitignore, + ignore: IliasIgnore, course_names: HashMap, ) -> Result { let cookie_store = CookieStore::default(); diff --git a/src/iliasignore.rs b/src/iliasignore.rs new file mode 100644 index 0000000..65efbf4 --- /dev/null +++ b/src/iliasignore.rs @@ -0,0 +1,66 @@ +use std::{path::{Path, PathBuf, Component}, ffi::OsString}; + +use anyhow::Result; +use ignore::gitignore::Gitignore; + +#[derive(Clone, Debug)] +pub struct IliasIgnore { + ignores: Vec +} + +impl IliasIgnore { + pub fn load(mut path: PathBuf) -> Result { + let mut ignores = Vec::new(); + let mut prefix = Vec::new(); + // example scenario: + // path = /KIT/ILIAS/SS 23/Next Generation Internet + // iliasignore in ILIAS/.iliasignore: prefix = SS 23/Next Generation Internet + // iliasignore in Next Generation Internet/.iliasignore: prefix = "" + loop { + let (ignore, error) = Gitignore::new(path.join(".iliasignore")); + if let Some(err) = error { + warning!(err); + } + if ignore.len() > 0 { + ignores.push(IgnoreFile { + ignore, + prefix: prefix.iter().fold(OsString::new(), |mut acc, el| { + acc.push(el); + acc.push("/"); + acc + }) + }); + } + if let Some(last) = path.components().last() { + match last { + Component::Normal(name) => prefix.insert(0, name.to_owned()), + _ => break + } + } + path.pop(); + } + Ok(IliasIgnore { + ignores + }) + } + + pub fn should_ignore(&self, path: &Path, is_dir: bool) -> bool { + for ignore_file in &self.ignores { + let mut full_path = ignore_file.prefix.clone(); + full_path.push(path.as_os_str()); + let matched = ignore_file.ignore.matched(&full_path, is_dir); + if matched.is_whitelist() { + return false; + } else if matched.is_ignore() { + return true; + } + } + false + } +} + +#[derive(Clone, Debug)] +struct IgnoreFile { + ignore: Gitignore, + prefix: OsString +} \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 23768f7..ba5a236 100644 --- a/src/main.rs +++ b/src/main.rs @@ -25,6 +25,8 @@ mod cli; use cli::*; mod ilias; use ilias::*; +mod iliasignore; +use iliasignore::*; use Object::*; mod queue; mod util; @@ -38,7 +40,7 @@ async fn main() { } } -async fn try_to_load_session(opt: Opt, ignore: Gitignore, course_names: HashMap) -> Result { +async fn try_to_load_session(opt: Opt, ignore: IliasIgnore, course_names: HashMap) -> Result { let session_path = opt.output.join(".iliassession"); let meta = tokio::fs::metadata(&session_path).await?; let modified = meta.modified()?; @@ -58,7 +60,7 @@ async fn try_to_load_session(opt: Opt, ignore: Gitignore, course_names: HashMap< } } -async fn login(opt: Opt, ignore: Gitignore, course_names: HashMap) -> Result { +async fn login(opt: Opt, ignore: IliasIgnore, course_names: HashMap) -> Result { // load .iliassession file if opt.keep_session { match try_to_load_session(opt.clone(), ignore.clone(), course_names.clone()) @@ -117,7 +119,7 @@ async fn real_main(mut opt: Opt) -> Result<()> { .context("failed to canonicalize output directory")?; // load .iliasignore file - let (ignore, error) = Gitignore::new(opt.output.join(".iliasignore")); + let ignore = IliasIgnore::load(opt.output.clone())?; // Load course_names.toml file let course_names_path = opt.output.join("course_names.toml"); @@ -134,10 +136,6 @@ async fn real_main(mut opt: Opt) -> Result<()> { HashMap::new() }; - if let Some(err) = error { - warning!(err); - } - queue::set_download_rate(opt.rate); let ilias = login(opt, ignore, course_names).await?; @@ -235,7 +233,7 @@ async fn process(ilias: Arc, path: PathBuf, obj: Object) -> Result<()> { } } // root path should not be matched - if relative_path.parent().is_some() && ilias.ignore.matched(relative_path, obj.is_dir()).is_ignore() { + if relative_path.parent().is_some() && ilias.ignore.should_ignore(relative_path, obj.is_dir()) { log!(1, "Ignored {}", relative_path.to_string_lossy()); return Ok(()); }