Compare commits

...

2 commits

Author SHA1 Message Date
3bdb914052 refactor: markers as enum parsed from lines
All checks were successful
Test / test (push) Successful in 4s
Fold Marker and IssueMarker into an enum.
Add functions to Line to parse into a Marker.
2024-09-18 22:41:57 +01:00
1e2a6e1804 test: add first tests for pattern matching
All checks were successful
Test / test (push) Successful in 3s
2024-09-18 22:11:15 +01:00
12 changed files with 179 additions and 83 deletions

1
.ignore Normal file
View file

@ -0,0 +1 @@
**/tests

View file

@ -2,9 +2,11 @@
use std::path::Path; use std::path::Path;
use anyhow::{Context, Result}; use anyhow::{Context, Result};
use model::{Config, FoundMarkers, Line, Marker, MarkerKind}; use model::{Config, FoundMarkers, Line, Marker};
use patterns::{issue_pattern, marker_pattern};
mod model; mod model;
mod patterns;
fn main() -> Result<()> { fn main() -> Result<()> {
println!("Forgejo TODO Checker!"); println!("Forgejo TODO Checker!");
@ -17,10 +19,8 @@ fn main() -> Result<()> {
)) ))
.repo(std::env::var("GITHUB_REPOSITORY").context("GITHUB_REPOSITORY")?) .repo(std::env::var("GITHUB_REPOSITORY").context("GITHUB_REPOSITORY")?)
.server(std::env::var("GITHUB_SERVER_URL").context("GITHUB_SERVER_URL")?) .server(std::env::var("GITHUB_SERVER_URL").context("GITHUB_SERVER_URL")?)
.prefix_pattern(regex::Regex::new(r"(#|//)\s*(TODO|FIXME)").context("prefix regex")?) .prefix_pattern(marker_pattern()?)
.issue_pattern( .issue_pattern(issue_pattern()?)
regex::Regex::new(r"( |)(\(|\(#)(?P<ISSUE_NUMBER>\d+)(\))").context("issue regex")?,
)
.maybe_auth_token(std::env::var("REPO_TOKEN").ok()) .maybe_auth_token(std::env::var("REPO_TOKEN").ok())
.build(); .build();
@ -37,7 +37,7 @@ fn main() -> Result<()> {
} }
} }
println!("{found_markers:?}"); println!("{found_markers:#?}");
// TODO: add authentication when provided // TODO: add authentication when provided
// let issues = ureq::get(&api).call()?.into_string()?; // let issues = ureq::get(&api).call()?.into_string()?;
@ -59,35 +59,16 @@ fn scan_file(file: &Path, config: &Config, found_markers: &mut FoundMarkers) ->
.file_read_to_string(file)? .file_read_to_string(file)?
.lines() .lines()
.enumerate() .enumerate()
.filter_map(|(n, line)| prefix_match(n, line, file, config)) .map(|(n, line)| {
.for_each(|marker| { Line::builder()
println!("- {}", marker.line().value()); .file(file.to_path_buf())
if let Some(issue) = config .num(n)
.issue_pattern() .value(line.to_owned())
.find(marker.line().value()) .build()
.map(|issue| issue.as_str()) })
.and_then(|issue| issue.parse::<usize>().ok()) .filter_map(|line| line.into_marker().ok())
{ .filter(|marker| !matches!(marker, Marker::Unmarked))
found_markers.add_issue_marker(marker.into_issue_marker(issue)); .for_each(|marker| found_markers.add_marker(marker));
} else {
found_markers.add_marker(marker);
}
});
Ok(()) Ok(())
} }
fn prefix_match(num: usize, line: &str, path: &Path, config: &Config) -> Option<Marker> {
let find = config.prefix_pattern().find(line)?;
let kind = match find.as_str() {
"TODO" => Some(MarkerKind::Todo),
"FIXME" => Some(MarkerKind::Fixme),
_ => None,
}?;
Some(
Marker::builder()
.kind(kind)
.file(path.to_path_buf())
.line(Line::builder().num(num).value(line.to_owned()).build())
.build(),
)
}

View file

@ -1,18 +1,47 @@
// //
#![allow(dead_code)] #![allow(dead_code)]
use std::path::{Path, PathBuf};
use anyhow::{Context, Result};
use bon::Builder; use bon::Builder;
use crate::patterns::{issue_pattern, marker_pattern};
use super::Marker;
#[derive(Debug, Builder)] #[derive(Debug, Builder)]
pub struct Line { pub struct Line {
file: PathBuf,
num: usize, num: usize,
value: String, value: String,
} }
impl Line { impl Line {
pub fn file(&self) -> &Path {
&self.file
}
pub fn num(&self) -> usize { pub fn num(&self) -> usize {
self.num self.num
} }
pub fn value(&self) -> &str { pub fn value(&self) -> &str {
&self.value &self.value
} }
pub fn into_marker(self) -> Result<Marker> {
if marker_pattern()?.find(&self.value).is_some() {
match issue_pattern()?.captures(&self.value) {
Some(capture) => {
let issue = capture
.name("ISSUE_NUMBER")
.context("ISSUE_NUMBER")?
.as_str()
.to_owned();
Ok(Marker::Valid(self, issue))
}
None => Ok(Marker::Invalid(self)),
}
} else {
Ok(Marker::Unmarked)
}
}
} }

View file

@ -1,16 +1,12 @@
use super::{IssueMarker, Marker}; use super::Marker;
// //
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub struct FoundMarkers { pub struct FoundMarkers {
markers: Vec<Marker>, markers: Vec<Marker>,
issue_markers: Vec<IssueMarker>,
} }
impl FoundMarkers { impl FoundMarkers {
pub fn add_marker(&mut self, marker: Marker) { pub fn add_marker(&mut self, marker: Marker) {
self.markers.push(marker); self.markers.push(marker);
} }
pub fn add_issue_marker(&mut self, issue_marker: IssueMarker) {
self.issue_markers.push(issue_marker);
}
} }

View file

@ -1,7 +0,0 @@
//
/// What type of comment #[derive(Debug)]
#[derive(Debug)]
pub enum MarkerKind {
Todo,
Fixme,
}

View file

@ -1,37 +1,11 @@
// //
#![allow(dead_code)] #![allow(dead_code)]
use std::path::{Path, PathBuf};
use bon::Builder;
use crate::model::Line; use crate::model::Line;
use super::{IssueMarker, MarkerKind}; #[derive(Debug)]
pub enum Marker {
/// Represents a TODO or FIXME comment that doesn't have any issue number Unmarked,
#[derive(Debug, Builder)] Invalid(Line),
pub struct Marker { Valid(Line, String),
/// What type of marker
kind: MarkerKind,
/// Path of the file
file: PathBuf,
/// The line from the file
line: Line,
}
impl Marker {
pub fn kind(&self) -> &MarkerKind {
&self.kind
}
pub fn file(&self) -> &Path {
&self.file
}
pub fn line(&self) -> &Line {
&self.line
}
pub fn into_issue_marker(self, issue: usize) -> IssueMarker {
IssueMarker::builder().marker(self).issue(issue).build()
}
} }

View file

@ -1,10 +1,7 @@
// //
mod found; mod found;
mod issue; mod issue;
mod kind;
mod marker; mod marker;
pub use found::FoundMarkers; pub use found::FoundMarkers;
pub use issue::IssueMarker;
pub use kind::MarkerKind;
pub use marker::Marker; pub use marker::Marker;

View file

@ -5,4 +5,5 @@ mod markers;
pub use config::Config; pub use config::Config;
pub use line::Line; pub use line::Line;
pub use markers::*; pub use markers::FoundMarkers;
pub use markers::Marker;

16
src/patterns/mod.rs Normal file
View file

@ -0,0 +1,16 @@
//
#[cfg(test)]
mod tests;
use anyhow::{Context as _, Result};
use regex::Regex;
/// The pattern to find a TODO or FIXME comment
pub fn marker_pattern() -> Result<Regex> {
regex::Regex::new(r"(#|//)\s*(TODO|FIXME)").context("prefix regex")
}
/// The pattern to find an issue number on an already found TODO or FIXME comment
pub fn issue_pattern() -> Result<Regex> {
regex::Regex::new(r"( |)(\(|\(#)(?P<ISSUE_NUMBER>\d+)(\))").context("issue regex")
}

View file

@ -0,0 +1,33 @@
//
use super::*;
#[test]
fn when_issue_should_find_number() -> anyhow::Result<()> {
//given
let line = " a line with a // TODO: (#13) issue number";
//when
let result = issue_pattern()?.captures(line);
//then
assert!(result.is_some());
let captures = result.unwrap();
let issue_number = captures.name("ISSUE_NUMBER").unwrap().as_str();
assert_eq!(issue_number, "13");
Ok(())
}
#[test]
fn when_no_issue_should_find_nothing() -> anyhow::Result<()> {
//given
let line = " a line with a // TODO: and no issue number";
//when
let result = issue_pattern()?.captures(line);
//then
assert!(result.is_none());
Ok(())
}

View file

@ -0,0 +1,70 @@
use super::*;
#[test]
fn when_todo_find_marker() -> anyhow::Result<()> {
//given
let line = " a line // TODO: with a marker";
//when
let result = marker_pattern()?.find(line);
//then
assert!(result.is_some());
Ok(())
}
#[test]
fn when_fixme_find_marker() -> anyhow::Result<()> {
//given
let line = " a line // FIXME: with a marker";
//when
let result = marker_pattern()?.find(line);
//then
assert!(result.is_some());
Ok(())
}
#[test]
fn when_no_marker_find_nothing() -> anyhow::Result<()> {
//given
let line = " a line with no marker";
//when
let result = marker_pattern()?.find(line);
//then
assert!(result.is_none());
Ok(())
}
#[test]
fn when_invalid_todo_find_nothing() -> anyhow::Result<()> {
//given
let line = " a line TODO: with no real marker";
//when
let result = marker_pattern()?.find(line);
//then
assert!(result.is_none());
Ok(())
}
#[test]
fn when_invalid_fixme_find_nothing() -> anyhow::Result<()> {
//given
let line = " a line FIXME: with no real marker";
//when
let result = marker_pattern()?.find(line);
//then
assert!(result.is_none());
Ok(())
}

View file

@ -0,0 +1,5 @@
//
use super::*;
mod issue;
mod marker;