diff --git a/Cargo.toml b/Cargo.toml index 1f41024..126f843 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,7 +9,10 @@ anyhow = "1.0" bon = "3.0" ignore = "0.4" file-format = { version = "0.26", features = ["reader-txt"] } -kxio = "1.2" + + +kxio = "2.1" + regex = "1.10" serde = { version = "1.0", features = ["derive"] } tokio = { version = "1.37", features = ["full"] } diff --git a/src/init.rs b/src/init.rs index 1cfda9e..22d2c98 100644 --- a/src/init.rs +++ b/src/init.rs @@ -3,17 +3,16 @@ use crate::model::Config; use crate::patterns::issue_pattern; use crate::printer::Printer; use anyhow::{Context, Result}; -use kxio::fs; -use kxio::network::Network; +use kxio::{fs::FileSystem, net::Net}; -pub fn init_config(printer: &impl Printer, net: Network) -> Result { +pub fn init_config<'net, 'fs>( + printer: &impl Printer, + fs: &'fs FileSystem, + net: &'net Net, +) -> Result> { let config = Config::builder() .net(net) - .fs(fs::new( - std::env::var("GITHUB_WORKSPACE") - .context("GITHUB_WORKSPACE")? - .into(), - )) + .fs(fs) .repo(std::env::var("GITHUB_REPOSITORY").context("GITHUB_REPOSITORY")?) .server(std::env::var("GITHUB_SERVER_URL").context("GITHUB_SERVER_URL")?) .issue_pattern(issue_pattern()?) diff --git a/src/issues/fetch.rs b/src/issues/fetch.rs index b10fd23..1734f3c 100644 --- a/src/issues/fetch.rs +++ b/src/issues/fetch.rs @@ -1,33 +1,28 @@ +// use std::collections::HashSet; -// use crate::model::Config; use anyhow::Result; -use kxio::network::{NetRequest, NetUrl}; use super::Issue; -pub async fn fetch_open_issues(config: &Config) -> Result> { +pub async fn fetch_open_issues<'net, 'fs>(config: &Config<'net, 'fs>) -> Result> { let server_url = config.server(); let repo = config.repo(); let url = format!("{server_url}/api/v1/repos/{repo}/issues?state=open"); - let request_builder = NetRequest::get(NetUrl::new(url)); + let net = config.net(); + let client = net.client(); + let request = client.get(url); let request = if let Some(auth_token) = config.auth_token() { - request_builder.header("Authorization", auth_token) + request.header("Authorization", auth_token) } else { - request_builder - } - .build(); + request + }; + // .build(); - let issues: HashSet = config - .net() - .get::>(request) - .await? // tarpaulin uncovered okay - .response_body() - .unwrap_or_default() - .into_iter() - .collect(); + let response = net.send(request).await?; + let issues: HashSet = response.json().await?; Ok(issues) } diff --git a/src/issues/tests.rs b/src/issues/tests.rs index 25beeea..3a2299f 100644 --- a/src/issues/tests.rs +++ b/src/issues/tests.rs @@ -1,3 +1,6 @@ +// +use super::*; + use std::collections::HashSet; use crate::tests::a_config; @@ -7,26 +10,39 @@ use super::*; use anyhow::Result; use kxio::network::StatusCode; +use std::collections::HashSet; + +use crate::tests::a_config; + +use kxio::net::{Method, Net, Url}; #[tokio::test] -async fn fetch_lists_issues() -> Result<()> { +async fn fetch_lists_issues() { //given - let mut net = kxio::network::MockNetwork::new(); - net.add_get_response( - "https://git.kemitix.net/api/v1/repos/kemitix/test/issues?state=open", - StatusCode::OK, - r#"[{"number":13},{"number":64}]"#, - ); - let config = a_config(net.into(), kxio::fs::temp()?)?; + let mock_net = kxio::net::mock(); + mock_net + .on(Method::GET) + .url( + Url::parse("https://git.kemitix.net/api/v1/repos/kemitix/test/issues?state=open") + .expect("parse url"), + ) + .respond( + mock_net + .response() + .status(200) + .body(r#"[{"number":13},{"number":64}]"#) + .expect("response body"), + ); + let fs = kxio::fs::temp().expect("temp fs"); + let net = Net::from(mock_net); + let config = a_config(&net, &fs).expect("config"); //when - let result = fetch_open_issues(&config).await?; + let result = fetch_open_issues(&config).await.expect("when"); //then assert_eq!( result, HashSet::from_iter(vec![Issue::new(13), Issue::new(64)]) ); - - Ok(()) } diff --git a/src/main.rs b/src/main.rs index 6d3f51b..8650777 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,9 +1,8 @@ // -use anyhow::{bail, Result}; +use anyhow::{bail, Context as _, Result}; use init::init_config; use issues::fetch_open_issues; -use kxio::network::Network; -use printer::Printer; +use printer::{Printer, StandardPrinter}; use scanner::{find_markers, DefaultFileScanner}; mod init; @@ -19,15 +18,23 @@ mod tests; #[tokio::main] #[cfg(not(tarpaulin_include))] async fn main() -> std::result::Result<(), Box> { - use printer::StandardPrinter; - Ok(run(&StandardPrinter, Network::new_real()).await?) + let github_workspace = std::env::var("GITHUB_WORKSPACE").context("GITHUB_WORKSPACE")?; + let fs = kxio::fs::new(github_workspace); + + let net = kxio::net::new(); + + Ok(run(&StandardPrinter, &fs, &net).await?) } -async fn run(printer: &impl Printer, net: Network) -> Result<()> { +async fn run( + printer: &impl Printer, + fs: &kxio::fs::FileSystem, + net: &kxio::net::Net, +) -> Result<()> { printer.println("Forgejo TODO Checker!"); - let config = init_config(printer, net)?; + let config = init_config(printer, fs, net)?; let issues = fetch_open_issues(&config).await?; let errors = find_markers(printer, &config, issues, &DefaultFileScanner)?; diff --git a/src/model/config.rs b/src/model/config.rs index 3c29743..a9569b4 100644 --- a/src/model/config.rs +++ b/src/model/config.rs @@ -2,21 +2,21 @@ use bon::Builder; use regex::Regex; -#[derive(Debug, Builder)] -pub struct Config { - net: kxio::network::Network, - fs: kxio::fs::FileSystem, +#[derive(Builder)] +pub struct Config<'net, 'fs> { + net: &'net kxio::net::Net, + fs: &'fs kxio::fs::FileSystem, repo: String, server: String, auth_token: Option, issue_pattern: Regex, } -impl Config { - pub fn net(&self) -> &kxio::network::Network { - &self.net +impl<'net, 'fs> Config<'net, 'fs> { + pub fn net(&self) -> &kxio::net::Net { + self.net } pub fn fs(&self) -> &kxio::fs::FileSystem { - &self.fs + self.fs } pub fn repo(&self) -> &str { &self.repo diff --git a/src/model/tests/config.rs b/src/model/tests/config.rs index 9e0779a..b5a3482 100644 --- a/src/model/tests/config.rs +++ b/src/model/tests/config.rs @@ -6,9 +6,9 @@ use crate::{patterns::issue_pattern, tests::a_config}; #[tokio::test] async fn with_config_get_net() -> Result<()> { //given - let net = kxio::network::Network::new_mock(); + let net = kxio::net::mock().into(); let fs = kxio::fs::temp()?; - let config = a_config(net, fs)?; + let config = a_config(&net, &fs)?; //when config.net(); @@ -21,9 +21,9 @@ async fn with_config_get_net() -> Result<()> { #[test] fn with_config_get_fs() -> Result<()> { //given - let net = kxio::network::Network::new_mock(); + let net = kxio::net::mock().into(); let fs = kxio::fs::temp()?; - let config = a_config(net, fs.clone())?; + let config = a_config(&net, &fs)?; //when let result = config.fs(); @@ -37,9 +37,9 @@ fn with_config_get_fs() -> Result<()> { #[test] fn with_config_get_issue_pattern() -> Result<()> { //given - let net = kxio::network::Network::new_mock(); + let net = kxio::net::mock().into(); let fs = kxio::fs::temp()?; - let config = a_config(net, fs)?; + let config = a_config(&net, &fs)?; //when let result = config.issue_pattern(); @@ -53,9 +53,9 @@ fn with_config_get_issue_pattern() -> Result<()> { #[test] fn with_config_get_server() -> Result<()> { //given - let net = kxio::network::Network::new_mock(); + let net = kxio::net::mock().into(); let fs = kxio::fs::temp()?; - let config = a_config(net, fs)?; + let config = a_config(&net, &fs)?; //when let result = config.server(); @@ -69,9 +69,9 @@ fn with_config_get_server() -> Result<()> { #[test] fn with_config_get_auth_token() -> Result<()> { //given - let net = kxio::network::Network::new_mock(); + let net = kxio::net::mock().into(); let fs = kxio::fs::temp()?; - let config = a_config(net, fs)?; + let config = a_config(&net, &fs)?; //when let result = config.auth_token(); @@ -85,9 +85,9 @@ fn with_config_get_auth_token() -> Result<()> { #[test] fn with_config_get_repo() -> Result<()> { //given - let net = kxio::network::Network::new_mock(); + let net = kxio::net::mock().into(); let fs = kxio::fs::temp()?; - let config = a_config(net, fs)?; + let config = a_config(&net, &fs)?; //when let result = config.repo(); diff --git a/src/scanner.rs b/src/scanner.rs index 44aa1b9..65e54e3 100644 --- a/src/scanner.rs +++ b/src/scanner.rs @@ -6,7 +6,7 @@ use crate::{ model::{Config, Line, Marker}, printer::Printer, }; -use anyhow::Result; +use anyhow::{Context as _, Result}; use file_format::FileFormat; use ignore::Walk; @@ -29,15 +29,17 @@ pub fn find_markers( let mut errors = 0; for file in Walk::new(config.fs().base()).flatten() { let path = file.path(); - if is_text_file(config, path)? { - errors += file_scanner.scan_file(path, config, printer, &issues)? + if is_text_file(config, path).context("is text file")? { + errors += file_scanner + .scan_file(path, config, printer, &issues) + .context("scan file")? } } Ok(errors) } fn is_text_file(config: &Config, path: &Path) -> Result { - Ok(config.fs().path_is_file(path)? + Ok(config.fs().path(path).is_file()? && FileFormat::from_file(path)? .media_type() .starts_with("text/")) @@ -56,7 +58,9 @@ impl FileScanner for DefaultFileScanner { let mut errors = 0; config .fs() - .file_read_to_string(file)? // tarpaulin uncovered okay + .file(file) + .reader()? + .to_string() // tarpaulin uncovered okay .lines() .enumerate() .map(|(n, line)| { diff --git a/src/tests/init.rs b/src/tests/init.rs index 750cc5a..7c109e5 100644 --- a/src/tests/init.rs +++ b/src/tests/init.rs @@ -2,32 +2,37 @@ use super::*; use assert2::let_assert; -use kxio::network::Network; +use kxio::net::Net; use model::Config; use patterns::issue_pattern; use printer::TestPrinter; #[test] -fn init_when_all_valid() -> anyhow::Result<()> { +fn init_when_all_valid() { //given + let fs = kxio::fs::temp().expect("temp fs"); + let _env = THE_ENVIRONMENT.lock(); - let fs = kxio::fs::temp()?; std::env::set_var("GITHUB_WORKSPACE", fs.base()); std::env::set_var("GITHUB_REPOSITORY", "repo"); std::env::set_var("GITHUB_SERVER_URL", "server"); - let net = Network::new_mock(); + + let mock_net = kxio::net::mock(); + let net = Net::from(mock_net); + let printer = TestPrinter::default(); + let expected = Config::builder() - .net(net.clone()) - .fs(kxio::fs::new(fs.base().to_path_buf())) + .net(&net) + .fs(&fs) .repo("repo".to_string()) .server("server".to_string()) - .issue_pattern(issue_pattern()?) + .issue_pattern(issue_pattern().expect("pattern")) .maybe_auth_token(Some("auth".to_string())) .build(); //when - let result = init_config(&printer, net)?; + let result = init_config(&printer, &fs, &net).expect("config"); //then assert_eq!(result.fs().base(), expected.fs().base()); @@ -37,65 +42,50 @@ fn init_when_all_valid() -> anyhow::Result<()> { result.issue_pattern().to_string(), expected.issue_pattern().to_string() ); - - Ok(()) } #[test] -fn init_when_no_workspace() -> anyhow::Result<()> { +fn init_when_no_repository() { //given + let fs = kxio::fs::temp().expect("temp fs"); + let _env = THE_ENVIRONMENT.lock(); - std::env::remove_var("GITHUB_WORKSPACE"); - std::env::set_var("GITHUB_REPOSITORY", "repo"); - std::env::set_var("GITHUB_SERVER_URL", "server"); - let printer = TestPrinter::default(); - - //when - let result = init_config(&printer, Network::new_mock()); - - //then - let_assert!(Err(e) = result); - assert_eq!(e.to_string(), "GITHUB_WORKSPACE"); - - Ok(()) -} - -#[test] -fn init_when_no_repository() -> anyhow::Result<()> { - //given - let _env = THE_ENVIRONMENT.lock(); - let fs = kxio::fs::temp()?; std::env::set_var("GITHUB_WORKSPACE", fs.base()); std::env::remove_var("GITHUB_REPOSITORY"); std::env::set_var("GITHUB_SERVER_URL", "server"); + let printer = TestPrinter::default(); + let mock_net = kxio::net::mock(); + let net = Net::from(mock_net); + //when - let result = init_config(&printer, Network::new_mock()); + let result = init_config(&printer, &fs, &net); //then let_assert!(Err(e) = result); assert_eq!(e.to_string(), "GITHUB_REPOSITORY"); - - Ok(()) } #[test] -fn init_when_no_server_url() -> anyhow::Result<()> { +fn init_when_no_server_url() { //given + let fs = kxio::fs::temp().expect("temp fs"); + let _env = THE_ENVIRONMENT.lock(); - let fs = kxio::fs::temp()?; std::env::set_var("GITHUB_WORKSPACE", fs.base()); std::env::set_var("GITHUB_REPOSITORY", "repo"); std::env::remove_var("GITHUB_SERVER_URL"); + let printer = TestPrinter::default(); + let mock_net = kxio::net::mock(); + let net = Net::from(mock_net); + //when - let result = init_config(&printer, Network::new_mock()); + let result = init_config(&printer, &fs, &net); //then let_assert!(Err(e) = result); assert_eq!(e.to_string(), "GITHUB_SERVER_URL"); - - Ok(()) } diff --git a/src/tests/mod.rs b/src/tests/mod.rs index 80ffef0..008c094 100644 --- a/src/tests/mod.rs +++ b/src/tests/mod.rs @@ -13,7 +13,10 @@ mod scanner; pub static THE_ENVIRONMENT: LazyLock> = LazyLock::new(|| Mutex::new(())); -pub fn a_config(net: kxio::network::Network, fs: kxio::fs::FileSystem) -> Result { +pub fn a_config<'net, 'fs>( + net: &'net kxio::net::Net, + fs: &'fs kxio::fs::FileSystem, +) -> Result> { Ok(Config::builder() .net(net) .fs(fs) diff --git a/src/tests/run.rs b/src/tests/run.rs index 295d143..229b7e6 100644 --- a/src/tests/run.rs +++ b/src/tests/run.rs @@ -2,84 +2,83 @@ use super::*; use anyhow::Result; -use kxio::network::{RequestBody, RequestMethod, SavedRequest, StatusCode}; use pretty_assertions::assert_eq; +use kxio::net::{Method, Net, Url}; use printer::TestPrinter; +use url::Url; #[tokio::test] -async fn run_with_some_invalids() -> Result<()> { +async fn run_with_some_invalids() /* -> Result<()> */ +{ //given - let mut net = kxio::network::MockNetwork::new(); - net.add_get_response( - "https://git.kemitix.net/api/v1/repos/kemitix/test/issues?state=open", - StatusCode::OK, - r#"[{"number": 13}]"#, - ); + let mock_net = kxio::net::mock(); + mock_net + .on(Method::GET) + .url( + Url::parse("https://git.kemitix.net/api/v1/repos/kemitix/test/issues?state=open") + .expect("parse url"), + ) + .respond( + mock_net + .response() + .status(200) + .body(r#"[{"number": 13}]"#) + .expect("response body"), + ); + let net = Net::from(mock_net); + + let fs = kxio::fs::temp().expect("temp fs"); + fs.file(&fs.base().join("file_with_invalids.txt")) + .write(include_str!("data/file_with_invalids.txt")) + .expect("write file with invalids"); + fs.file(&fs.base().join("file_with_valids.txt")) + .write(include_str!("data/file_with_valids.txt")) + .expect("write file with valids"); + let _env = THE_ENVIRONMENT.lock(); - let fs = kxio::fs::temp()?; - fs.file_write( - &fs.base().join("file_with_invalids.txt"), - include_str!("data/file_with_invalids.txt"), - )?; - fs.file_write( - &fs.base().join("file_with_valids.txt"), - include_str!("data/file_with_valids.txt"), - )?; std::env::set_var("GITHUB_WORKSPACE", fs.base()); std::env::set_var("GITHUB_REPOSITORY", "kemitix/test"); std::env::set_var("GITHUB_SERVER_URL", "https://git.kemitix.net"); //when - let result = run(&TestPrinter::default(), net.clone().into()).await; + let result = run(&TestPrinter::default(), &fs, &net).await; //then assert!(result.is_err()); // there is an invalid file - let requests = net.requests(); - assert_eq!( - requests, - vec![SavedRequest::new( - RequestMethod::Get, - "https://git.kemitix.net/api/v1/repos/kemitix/test/issues?state=open", - RequestBody::None, - )] - ); - - Ok(()) } #[tokio::test] -async fn run_with_no_invalids() -> Result<()> { +async fn run_with_no_invalids() { //given - let mut net = kxio::network::MockNetwork::new(); - net.add_get_response( - "https://git.kemitix.net/api/v1/repos/kemitix/test/issues?state=open", - StatusCode::OK, - r#"[{"number":23},{"number":43}]"#, - ); + let mock_net = kxio::net::mock(); + mock_net + .on(Method::GET) + .url( + Url::parse("https://git.kemitix.net/api/v1/repos/kemitix/test/issues?state=open") + .expect("parse url"), + ) + .respond( + mock_net + .response() + .status(200) + .body(r#"[{"number":23},{"number":43}]"#) + .expect("response body"), + ); + let net = Net::from(mock_net); + + let fs = kxio::fs::temp().expect("temp fs"); + fs.file(&fs.base().join("file_with_valids.txt")) + .write(include_str!("data/file_with_valids.txt")) + .expect("write file with valids"); + let _env = THE_ENVIRONMENT.lock(); - let fs = kxio::fs::temp()?; - fs.file_write( - &fs.base().join("file_with_valids.txt"), - include_str!("data/file_with_valids.txt"), - )?; std::env::set_var("GITHUB_WORKSPACE", fs.base()); std::env::set_var("GITHUB_REPOSITORY", "kemitix/test"); std::env::set_var("GITHUB_SERVER_URL", "https://git.kemitix.net"); //when - let result = run(&TestPrinter::default(), net.clone().into()).await; + let result = run(&TestPrinter::default(), &fs, &net).await; //then assert!(result.is_ok()); // there is an invalid file - let requests = net.requests(); - assert_eq!( - requests, - vec![SavedRequest::new( - RequestMethod::Get, - "https://git.kemitix.net/api/v1/repos/kemitix/test/issues?state=open", - RequestBody::None, - )] - ); - - Ok(()) } diff --git a/src/tests/scanner.rs b/src/tests/scanner.rs index b6a30c0..201a984 100644 --- a/src/tests/scanner.rs +++ b/src/tests/scanner.rs @@ -1,42 +1,44 @@ -use crate::scanner::FileScanner; - // use super::*; -use std::{cell::RefCell, collections::HashSet, fs::File, io::Write, path::PathBuf}; +use std::{cell::RefCell, collections::HashSet, path::PathBuf}; + +use crate::scanner::FileScanner; use issues::Issue; +use kxio::net::Net; use model::Config; use patterns::issue_pattern; use pretty_assertions::assert_eq; use printer::TestPrinter; #[test] -fn find_markers_in_dir() -> anyhow::Result<()> { +fn find_markers_in_dir() { //given - let fs = kxio::fs::temp()?; + let fs = kxio::fs::temp().expect("temp fs"); let file_with_invalids = fs.base().join("file_with_invalids.txt"); - fs.file_write( - &file_with_invalids, - include_str!("data/file_with_invalids.txt"), - )?; - fs.file_write( - &fs.base().join("file_with_valids.txt"), - include_str!("data/file_with_valids.txt"), - )?; + fs.file(&file_with_invalids) + .write(include_str!("data/file_with_invalids.txt")) + .expect("write with invalids"); + fs.file(&fs.base().join("file_with_valids.txt")) + .write(include_str!("data/file_with_valids.txt")) + .expect("write with valids"); + + let mock_net = kxio::net::mock(); + let net = Net::from(mock_net); let config = Config::builder() - .net(kxio::network::Network::new_mock()) - .fs(fs.clone()) + .net(&net) + .fs(&fs) .server("".to_string()) .repo("".to_string()) - .issue_pattern(issue_pattern()?) + .issue_pattern(issue_pattern().expect("pattern")) .build(); let issues = HashSet::from_iter(vec![Issue::new(23), Issue::new(43)]); let printer = TestPrinter::default(); //when - let errors = find_markers(&printer, &config, issues, &DefaultFileScanner)?; + let errors = find_markers(&printer, &config, issues, &DefaultFileScanner).expect("when"); //then assert_eq!( @@ -45,37 +47,37 @@ fn find_markers_in_dir() -> anyhow::Result<()> { "- Issue number missing: file_with_invalids.txt#3:\n It contains a todo comment: // TODO: this is it\n", "- Issue number missing: file_with_invalids.txt#5:\n It also contains a fix-me comment: // FIXME: and this is it\n", "- Closed/Invalid Issue: (3) file_with_invalids.txt#9:\n We also have a todo comment: // TODO: (#3) and it has an issue number, but it is closed\n", - format!(">> 3 errors in {}\n", file_with_invalids.strip_prefix(fs.base())?.to_string_lossy()).as_str() + format!(">> 3 errors in {}\n", file_with_invalids.strip_prefix(fs.base()).expect("strip prefix").to_string_lossy()).as_str() ] ); assert_eq!(errors, 3); - - Ok(()) } #[test] -fn skips_binary_files() -> Result<()> { +fn skips_binary_files() { //given - let fs = kxio::fs::temp()?; + let fs = kxio::fs::temp().expect("temp fs"); let binary_path = fs.base().join("binary_file.bin"); - let mut binary_file = File::create(binary_path)?; - binary_file.write_all(&[0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9])?; + fs.file(&binary_path) + .write([0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) + .expect("write binary file"); let text_path = fs.base().join("text_file.txt"); - fs.file_write(&text_path, "text contents")?; + fs.file(&text_path) + .write("text contents") + .expect("write file"); - let net = kxio::network::Network::new_mock(); - let config = a_config(net, fs)?; + let mock_net = kxio::net::mock(); + let net = Net::from(mock_net); + let config = a_config(&net, &fs).expect("config"); let issues = HashSet::new(); let file_scanner = TestFileScanner::default(); let printer = TestPrinter::default(); //when - find_markers(&printer, &config, issues, &file_scanner)?; + find_markers(&printer, &config, issues, &file_scanner).expect("when"); //then assert_eq!(file_scanner.scanned.take(), vec![text_path]); - - Ok(()) } #[derive(Default)]