From e4f02491e8f7e5746e86ff951b6f758279209c28 Mon Sep 17 00:00:00 2001 From: mars Date: Sat, 10 Feb 2024 16:30:38 -0700 Subject: [PATCH] Initial copyright notice parsing --- src/main.rs | 114 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 114 insertions(+) diff --git a/src/main.rs b/src/main.rs index 6890bcd..09d0240 100644 --- a/src/main.rs +++ b/src/main.rs @@ -62,6 +62,90 @@ impl Language { } } +/// A single copyright notice on a source file. +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct Copyright { + /// The name of the copyright holder. + pub holder: String, + + /// The first year of copyright holding. + pub first_year: usize, + + /// The last year of copyright holding. + /// + /// May be the same as `first_year`, in which case this is omitted in + /// formatting. + pub last_year: usize, +} + +impl Copyright { + /// Attempts to parse a copyright notice from a string. + pub fn parse(src: &str) -> Result { + let src = src.trim(); + + if src.is_empty() { + return Err(CopyrightError::Empty); + } + + static VALID_PREFIXES: &'static [&'static str] = + &["© ", "Copyright © ", "Copyright (c) ", "Copyright (C) "]; + + let mut valid_prefixes = VALID_PREFIXES.iter(); + + let body = loop { + let Some(test_prefix) = valid_prefixes.next() else { + // options are exhausted, we didn't find a valid prefix + return Err(CopyrightError::InvalidPrefix); + }; + + if let Some(body) = src.strip_prefix(*test_prefix) { + break body; + } + }; + + // retrieve years range and copyright holder + let (years, holder) = body.split_once(" ").ok_or(CopyrightError::MissingHolder)?; + + let holder = holder.to_string(); + + // attempt to parse single given year + if let Ok(year) = years.parse() { + return Ok(Copyright { + holder, + first_year: year, + last_year: year, + }); + } + + let Some((first_year, last_year)) = years.split_once("-") else { + return Err(CopyrightError::MalformedYear); + }; + + let first_year = first_year + .parse() + .map_err(|_| CopyrightError::MalformedYear)?; + + let last_year = last_year + .parse() + .map_err(|_| CopyrightError::MalformedYear)?; + + Ok(Copyright { + holder, + first_year, + last_year, + }) + } +} + +/// An error in copyright parsing or validation. +#[derive(Clone, Copy, Debug)] +pub enum CopyrightError { + Empty, + InvalidPrefix, + MissingHolder, + MalformedYear, +} + fn main() { println!("Hello, world!"); } @@ -83,4 +167,34 @@ mod tests { let lines = Language::RUST.read_header(&mut src.as_slice()).unwrap(); assert_eq!(lines, vec!["header content"]); } + + #[test] + fn parse_copyright() { + let src = "Copyright (c) 2024 Marceline Cramer"; + let cr = Copyright::parse(src).unwrap(); + + assert_eq!( + cr, + Copyright { + holder: "Marceline Cramer".into(), + first_year: 2024, + last_year: 2024, + } + ); + } + + #[test] + fn parse_copyright_year_range() { + let src = "Copyright (c) 2023-2024 Marceline Cramer"; + let cr = Copyright::parse(src).unwrap(); + + assert_eq!( + cr, + Copyright { + holder: "Marceline Cramer".into(), + first_year: 2023, + last_year: 2024, + } + ); + } }