Skip to content

Commit

Permalink
Merge pull request #350 from o2sh/feature/macro_package_managers
Browse files Browse the repository at this point in the history
 Refactoring of package manager module
  • Loading branch information
o2sh authored Dec 20, 2020
2 parents 20172d9 + db61e8e commit 7de7cc2
Show file tree
Hide file tree
Showing 4 changed files with 131 additions and 131 deletions.
42 changes: 11 additions & 31 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,30 +105,20 @@ Remarks:

### Adding support for a new package manager

Any package manager is supported, as long as there is a file you can get the dependencies from.
Any package manager is supported, as long as it is associated to a file that can be parsed to extract the number of dependencies from.

To add a new package manager, make sure you follow these steps:
1. in `src/onefetch/deps/package_manager.rs`, add the name of your package manager to the `PackageManager` enum
```rust
pub enum PackageManager {
// ...
Cargo,
// ...
}
```
To add a new package manager, make sure to follow these steps:

2. in `src/onefetch/deps/package_manager.rs`, add a `writeln` macro call to the `fmt` function
```rust
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match *self {
// ...
PackageManager::Cargo => write!(f, "cargo"),
// ...
}
}
```
1. Add a new entry in the `define_package_managers!` macro in [package_manager.rs](src/onefetch/deps/package_manager.rs).

**Example**:

`{ Cargo, "cargo", [ ("Cargo.toml", cargo) ] },`

The first item `Cargo` corresponds to the name of the package manager. The second item `cargo` is the display name. Then we have the name of the package manager file that will be parsed: `Cargo.toml` along with its parser `cargo` (cf. step 2), notice that the third item is an array of tuple in case the package manager has multiple parsers (e.g. pip).

2. in `src/onefetch/deps/package_parser.rs`: create a function that takes an input of type `&str` representing the content of the package manager's file, and returns a `usize` as its number of dependencies.

3. in `src/onefetch/deps/package_parser.rs`, add a function whose name corresponds to your manager. This function should take in a `string` as the contents of the package manager file, and return `usize` as the number of dependencies. You should also make sure you catch any edge cases, such as the absence of a `dependencies` field.
```rust
pub fn cargo(contents: &str) -> Result<usize> {
let parsed = contents.parse::<Value>()?;
Expand All @@ -141,16 +131,6 @@ pub fn cargo(contents: &str) -> Result<usize> {
}
```

4. in `src/onefetch/deps/mod.rs`, in the `new` method of the `DependencyDetector` impl, insert your new package managers information
```rust
package_managers.insert(
String::from("Cargo.toml"), // File name

// Parser function, then the corresponding element from the PackageManager enum
(package_parser::cargo, package_manager::PackageManager::Cargo),
);
```

### Adding translation for README.md

In order to make Onefetch more accessible for non English-speakers, we are seeking the help of multilingual contributors willing to translate the README.md in their native tongue.
Expand Down
27 changes: 1 addition & 26 deletions src/onefetch/deps/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ use {
};

pub mod package_manager;
mod package_parser;

type DependencyParser = fn(&str) -> Result<usize>;

Expand All @@ -15,31 +14,7 @@ pub struct DependencyDetector {

impl DependencyDetector {
pub fn new() -> Self {
let mut package_managers: HashMap<
String,
(DependencyParser, package_manager::PackageManager),
> = HashMap::new();

package_managers.insert(
String::from("Cargo.toml"),
(package_parser::cargo, package_manager::PackageManager::Cargo),
);
package_managers.insert(
String::from("go.mod"),
(package_parser::go_modules, package_manager::PackageManager::GoModules),
);
package_managers.insert(
String::from("package.json"),
(package_parser::npm, package_manager::PackageManager::Npm),
);
package_managers.insert(
String::from("requirements.txt"),
(package_parser::pip, package_manager::PackageManager::Pip),
);
package_managers.insert(
String::from("pubspec.yaml"),
(package_parser::pub_packages, package_manager::PackageManager::Pub),
);
let package_managers = package_manager::build();

DependencyDetector { package_managers }
}
Expand Down
138 changes: 119 additions & 19 deletions src/onefetch/deps/package_manager.rs
Original file line number Diff line number Diff line change
@@ -1,22 +1,122 @@
use strum::EnumIter;

#[derive(PartialEq, EnumIter)]
pub enum PackageManager {
Cargo,
GoModules,
Npm,
Pip,
Pub,
}

impl std::fmt::Display for PackageManager {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match *self {
PackageManager::Cargo => write!(f, "cargo"),
PackageManager::GoModules => write!(f, "go modules"),
PackageManager::Npm => write!(f, "npm"),
PackageManager::Pip => write!(f, "pip"),
PackageManager::Pub => write!(f, "pub"),
use crate::onefetch::error::*;
use std::collections::HashMap;
use {regex::Regex, strum::EnumIter, toml::Value, yaml_rust::YamlLoader};

macro_rules! define_package_managers {
($( { $name:ident, $display:literal, [$(($file:literal, $parser:expr))+] } ),+ ,) => {

#[derive(PartialEq, EnumIter)]
pub enum PackageManager {
$(
$name ,
)+
}

impl std::fmt::Display for PackageManager {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match *self {
$( PackageManager::$name => write!(f, $display), )+
}
}
}

pub fn build() -> HashMap<String, (fn(&str) -> Result<usize>, PackageManager)> {
let mut package_managers: HashMap<
String,
(fn(&str) -> Result<usize>, PackageManager)
> = HashMap::new();

$(
$(
package_managers.insert(String::from($file), ($parser, PackageManager::$name));
)+
)+

package_managers
}

}
}

define_package_managers! {
{ Cargo, "cargo", [ ("Cargo.toml", cargo) ] },
{ GoModules, "go modules", [ ("go.mod", go_modules) ] },
{ Npm, "npm", [ ("package.json", npm) ] },
{ Pip, "pip", [ ("requirements.txt", pip_requirement) ("pyproject.toml", pip_pyproject) ("Pipfile", pip_pipfile) ] },
{ Pub, "pub", [ ("pubspec.yaml", pub_packages) ] },
}

fn cargo(contents: &str) -> Result<usize> {
let parsed = contents.parse::<Value>()?;
let count = parsed.get("dependencies");

match count {
Some(val) => Ok(val.as_table().unwrap().len()),
None => Ok(0),
}
}

fn go_modules(contents: &str) -> Result<usize> {
let mut count = 0;
let mut start = false;
for line in contents.lines() {
if line.contains("require") {
start = true;
continue;
}

if start && line.contains(')') {
break;
}

if start {
count += 1;
}
}

Ok(count)
}

fn npm(contents: &str) -> Result<usize> {
let parsed = json::parse(contents)?;

Ok(parsed["dependencies"].len())
}

fn pip_requirement(contents: &str) -> Result<usize> {
let count = Regex::new(r"(^|\n)[A-z]+")?.find_iter(contents).count();

Ok(count)
}

fn pip_pyproject(contents: &str) -> Result<usize> {
let parsed = contents.parse::<Value>()?;
let count = parsed
.get("tool")
.and_then(|tool| tool.get("poetry"))
.and_then(|poetry| poetry.get("dependencies"));
match count {
Some(val) => Ok(val.as_table().unwrap().len()),
None => Ok(0),
}
}

fn pip_pipfile(contents: &str) -> Result<usize> {
let parsed = contents.parse::<Value>()?;
let count = parsed.get("packages");

match count {
Some(val) => Ok(val.as_table().unwrap().len()),
None => Ok(0),
}
}

fn pub_packages(contents: &str) -> Result<usize> {
match YamlLoader::load_from_str(contents) {
Ok(parsed) => match &parsed[0]["dependencies"].as_hash() {
Some(deps) => Ok(deps.len()),
None => Ok(0),
},
Err(_) => Ok(0),
}
}
55 changes: 0 additions & 55 deletions src/onefetch/deps/package_parser.rs

This file was deleted.

0 comments on commit 7de7cc2

Please sign in to comment.