Skip to content

Commit 756624c

Browse files
committed
Implemented Requirements Module and added test file for it
1 parent 2e10ae5 commit 756624c

File tree

3 files changed

+124
-13
lines changed

3 files changed

+124
-13
lines changed

src/requirements/mod.rs

Lines changed: 91 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,45 @@ use std::fmt::Display;
44
use std::fs::{read_to_string, File};
55
use std::path::PathBuf;
66

7+
use anyhow::{bail, Result};
8+
use package_version::PackageVersion;
9+
use pomsky_macro::pomsky;
10+
use regex::Regex;
11+
12+
static REQUIREMENTS_LINE_PARSER: &str = pomsky!(
13+
"v"?
14+
(
15+
:op("==" | ">=" | "<=")
16+
)
17+
);
18+
719
pub enum PyRequirementsOperator {
820
EqualTo,
21+
GreaterThan,
22+
LesserThan,
23+
}
24+
25+
impl PyRequirementsOperator {
26+
/// Creates a new `PyRequirementsOperator`
27+
///
28+
/// # Usage
29+
/// ```
30+
/// let a = PyRequirementsOperator::new("==").unwrap(); // Returns PyRequirementsOperator::EqualTo
31+
/// let b = PyRequirementsOperator::new("BigChungus"); // Returns an Err
32+
/// let c = PyRequirementsOperator::new("!!").unwrap(); // Also returns an Err
33+
/// ```
34+
fn new(op: &str) -> Result<Self, String> {
35+
if op.len() > 2 {
36+
return Err(format!("Operator is {} long", op.len()));
37+
}
38+
39+
match op {
40+
"==" => Ok(Self::EqualTo),
41+
">=" => Ok(Self::GreaterThan),
42+
"<=" => Ok(Self::LesserThan),
43+
_ => Err(format!("Unknown Operator: {}", op)),
44+
}
45+
}
946
}
1047

1148
impl Default for PyRequirementsOperator {
@@ -21,6 +58,8 @@ impl Display for PyRequirementsOperator {
2158
"{}",
2259
match self {
2360
Self::EqualTo => "==",
61+
Self::GreaterThan => ">=",
62+
Self::LesserThan => "<=",
2463
}
2564
)
2665
}
@@ -29,13 +68,34 @@ impl Display for PyRequirementsOperator {
2968
/// Represents a module in a `requirements.txt` file
3069
pub struct PyRequirementsModule {
3170
pub package: String,
32-
pub version: String,
71+
pub version: PackageVersion,
3372
pub operator: PyRequirementsOperator,
3473
}
3574

3675
impl PyRequirementsModule {
37-
fn new(raw: String) -> Self {
38-
todo!();
76+
fn new(raw: &str) -> Result<Self> {
77+
let regex = Regex::new(REQUIREMENTS_LINE_PARSER).unwrap();
78+
let res = match regex.captures(raw) {
79+
Some(caps) => caps,
80+
None => bail!("unable to parse line"),
81+
};
82+
83+
let op = res.name("op").unwrap();
84+
let (op_start, op_end) = (op.start(), op.end());
85+
86+
Ok(Self {
87+
operator: match PyRequirementsOperator::new(
88+
res.name("op").unwrap().as_str(),
89+
) {
90+
Ok(op) => op,
91+
Err(err) => bail!("Op Parsing returned an error: {}", err),
92+
},
93+
package: raw[..op_start].to_string(),
94+
version: match PackageVersion::new(&raw[op_end..]) {
95+
Ok(ver) => ver,
96+
Err(err) => bail!("Package Versioner returned an error: {}", err),
97+
},
98+
})
3999
}
40100
}
41101

@@ -53,19 +113,37 @@ pub struct PyRequirements {
53113

54114
impl PyRequirements {
55115
pub fn new(path: &PathBuf) -> Result<(), String> {
116+
// Check if the path specified is a file
56117
if !path.is_file() {
57118
return Err(format!("{:?} is not a file!", path.to_str().unwrap()));
58-
} else {
59-
let raw: Vec<String> = read_to_string(&path)
60-
.expect(
61-
format!("Unable to read file: {:?}", path.to_str().unwrap())
62-
.as_str(),
63-
)
64-
.split("\n")
65-
.map(|item| item.to_string())
66-
.collect();
67119
}
68120

69-
return Ok(());
121+
// Then check if that file is a "requirements.txt" file
122+
// TODO: Use some magic to see if the file can be parsed
123+
// and then use that to check instead of this
124+
if !path.ends_with("requirements.txt") {
125+
return Err(format!(
126+
"File specified is not a 'requirements.txt' file: {:?}",
127+
path.to_str().unwrap()
128+
));
129+
}
130+
131+
let binding = read_to_string(&path).expect(
132+
format!("Unable to read file: {:?}", path.to_str().unwrap()).as_str(),
133+
);
134+
135+
let raw: Vec<&str> = binding.split("\n").collect();
136+
let mut requirements = Vec::<PyRequirementsModule>::new();
137+
138+
for (lineno, line) in raw.iter().enumerate() {
139+
match PyRequirementsModule::new(line) {
140+
Ok(py_mod) => requirements.push(py_mod),
141+
Err(err) => {
142+
println!("Unable to parse line {}: {}", lineno, err)
143+
}
144+
}
145+
}
146+
147+
Ok(())
70148
}
71149
}

test/README.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
This folder is for testing purposes. All files in this directory (except this README)
2+
has been put here to test this codebase's functions
3+
4+
- Kiwifruit

test/requirements.txt

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
aiohttp==3.8.1
2+
aiosignal==1.2.0
3+
async-timeout==4.0.2
4+
attrs==21.4.0
5+
beautifulsoup4==4.10.0
6+
black==22.3.0
7+
certifi==2021.10.8
8+
charset-normalizer==2.0.12
9+
click==8.1.2
10+
commonmark==0.9.1
11+
expiringdict==1.2.1
12+
frozenlist==1.3.0
13+
idna==3.3
14+
multidict==6.0.2
15+
mypy-extensions==0.4.3
16+
NHentai-API==0.0.19
17+
pathspec==0.9.0
18+
platformdirs==2.5.1
19+
Pygments==2.11.2
20+
PyYAML==6.0
21+
requests==2.27.1
22+
retrying==1.3.3
23+
rich==12.1.0
24+
six==1.16.0
25+
soupsieve==2.3.2
26+
textual==0.1.17
27+
tomli==2.0.1
28+
urllib3==1.26.9
29+
yarl==1.7.2

0 commit comments

Comments
 (0)