Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Parse dependency requirements #80

Draft
wants to merge 8 commits into
base: latest
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions crates/ring-cli/src/list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ pub fn handle_command(core: &RingCore, args: &ArgMatches) -> anyhow::Result<()>
.map(lscolors::Style::to_owo_colors_style)
.unwrap_or_default();

let mut tags: BTreeSet<&'static Tag> = BTreeSet::new();
let mut tags: BTreeSet<Tag> = BTreeSet::new();

for project in detector.detect_at(&entry.path().normalize()) {
tags.extend(project?.tags());
Expand All @@ -64,7 +64,7 @@ pub fn handle_command(core: &RingCore, args: &ArgMatches) -> anyhow::Result<()>
}
} else {
let file_name = path.file_name().and_then(|s| s.to_str()).unwrap();
let mut tags: BTreeSet<&'static Tag> = BTreeSet::new();
let mut tags: BTreeSet<Tag> = BTreeSet::new();

for project in detector.detect_at(&path) {
tags.extend(project?.tags());
Expand Down
3 changes: 1 addition & 2 deletions crates/ring-cli/src/modules/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@ use ring_core::RingCore;

pub fn build_command() -> Command {
Command::new("modules")
.aliases(["module"])
.visible_aliases(["mod"])
.aliases(["mod"])
.subcommand_required(true)
.subcommands([
list::build_command()
Expand Down
7 changes: 5 additions & 2 deletions crates/ring-cli/src/projects/mod.rs
Original file line number Diff line number Diff line change
@@ -1,24 +1,27 @@
mod current;
mod list;
mod tree;

use clap::{ArgMatches, Command};
use ring_core::RingCore;

pub fn build_command() -> Command {
Command::new("projects")
.aliases(["project"])
.visible_aliases(["prj"])
.aliases(["prj"])
.visible_aliases(["project"])
.subcommand_required(true)
.subcommands([
current::build_command(),
list::build_command(),
tree::build_command(),
])
}

pub fn handle_command(core: &RingCore, args: &ArgMatches) -> anyhow::Result<()> {
match args.subcommand() {
Some(("current", _)) => current::handle_command(core),
Some(("list", args)) => list::handle_command(core, args),
Some(("tree", args)) => tree::handle_command(core, args),
_ => unreachable!()
}
}
52 changes: 52 additions & 0 deletions crates/ring-cli/src/projects/tree.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
use std::collections::VecDeque;
use std::env;
use std::path::PathBuf;
use clap::{arg, value_parser, ArgMatches, Command};
use itertools::Itertools;
use tracing::warn;
use ring_cli_formatters::ListFormatter;
use ring_core::RingCore;
use ring_utils::Normalize;

pub fn build_command() -> Command {
Command::new("tree")
.arg(arg!([path])
.value_parser(value_parser!(PathBuf)))
}

pub fn handle_command(core: &RingCore, args: &ArgMatches) -> anyhow::Result<()> {
let current_dir = env::current_dir()?.normalize();
let path = args.get_one::<PathBuf>("path")
.map(|path| path.resolve(&current_dir))
.unwrap_or(current_dir);

let detector = core.project_detector();
let mut projects = detector.detect_from(&path)
.collect::<anyhow::Result<VecDeque<_>>>()?;

if projects.is_empty() {
warn!("No matching project found");
return Ok(());
}

// Print current project as root
let mut list = ListFormatter::new();

for project in &projects {
list.add_row([
&project.name(),
&project.tags().iter().join("/")
]);
}

println!("{list}");

// Recursively print their deps
while let Some(prj) = projects.pop_front() {
for dep in prj.dependencies() {
println!("+ {dep}");

Check failure

Code scanning / clippy

std::result::Result<ring_utils::Dependency, anyhow::Error> doesn't implement std::fmt::Display Error

std::result::Result<ring_utils::Dependency, anyhow::Error> doesn't implement std::fmt::Display

Check failure

Code scanning / clippy

std::result::Result<ring_utils::Dependency, anyhow::Error> doesn't implement std::fmt::Display Error

std::result::Result<ring_utils::Dependency, anyhow::Error> doesn't implement std::fmt::Display
}
}

Ok(())
}
3 changes: 2 additions & 1 deletion crates/ring-core/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
pub use combined_detector::CombinedDetector;
use ring_traits::{Module, Project, Scope, Tagged};
use ring_traits::{Module, Project, Scope};
use ring_utils::Tagged;
use std::rc::Rc;

#[cfg(feature = "js")]
Expand Down
17 changes: 15 additions & 2 deletions crates/ring-js/src/constants.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::PackageManager;
use owo_colors::AnsiColors;
use owo_colors::DynColors::Ansi;
use ring_utils::Tag;
use crate::PackageManager;

pub const MANIFEST: &str = "package.json";
pub const PACKAGE_MANAGERS: [PackageManager; 3] = [
Expand All @@ -10,4 +10,17 @@ pub const PACKAGE_MANAGERS: [PackageManager; 3] = [
PackageManager::Yarn,
];

pub const JS_TAG: Tag = Tag::with_color("js", Ansi(AnsiColors::Yellow));
#[inline]
pub fn js_tag() -> Tag {
Tag::from("js").with_color(Ansi(AnsiColors::Yellow))
}

#[inline]
pub fn dev_tag() -> Tag {
Tag::from("dev").with_color(Ansi(AnsiColors::Blue))
}

#[inline]
pub fn optional_tag() -> Tag {
Tag::from("optional").with_color(Ansi(AnsiColors::Magenta))
}
1 change: 1 addition & 0 deletions crates/ring-js/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ mod project;
mod project_detector;
mod scope;
mod scope_detector;
mod utils;

pub use package_manager::PackageManager;
pub use package_manifest::PackageManifest;
Expand Down
20 changes: 18 additions & 2 deletions crates/ring-js/src/package_manifest.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
use std::io::Read;
use anyhow::Context;
use ring_traits::Manifest;
use semver::Version;
use serde::Deserialize;
use ring_traits::Manifest;
use std::collections::BTreeMap;
use std::io::Read;

#[derive(Debug, Deserialize, Eq, PartialEq)]
pub struct PackageManifest {
Expand All @@ -11,6 +12,12 @@ pub struct PackageManifest {
pub version: Option<Version>,
#[serde(default)]
pub workspaces: Vec<String>,
#[serde(default)]
pub dependencies: BTreeMap<String, String>,
#[serde(default, rename="devDependencies")]
pub dev_dependencies: BTreeMap<String, String>,
#[serde(default, rename="optionalDependencies")]
pub optional_dependencies: BTreeMap<String, String>,
}

impl Manifest for PackageManifest {
Expand Down Expand Up @@ -39,6 +46,9 @@ mod tests {
name: "test".to_string(),
version: None,
workspaces: Vec::new(),
dependencies: BTreeMap::default(),
dev_dependencies: BTreeMap::default(),
optional_dependencies: BTreeMap::default(),
});
}

Expand All @@ -53,6 +63,9 @@ mod tests {
name: "test".to_string(),
version: Some(Version::new(1, 0, 0)),
workspaces: Vec::new(),
dependencies: BTreeMap::default(),
dev_dependencies: BTreeMap::default(),
optional_dependencies: BTreeMap::default(),
});
}

Expand All @@ -70,6 +83,9 @@ mod tests {
"packages/test-a".to_string(),
"packages/test-b".to_string()
],
dependencies: BTreeMap::default(),
dev_dependencies: BTreeMap::default(),
optional_dependencies: BTreeMap::default(),
});
}
}
32 changes: 26 additions & 6 deletions crates/ring-js/src/project.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
use crate::constants::JS_TAG;
use crate::constants::{dev_tag, js_tag, optional_tag};
use crate::package_manifest::PackageManifest;
use ring_traits::{Project, Tagged};
use ring_utils::{NormalizedPath, NormalizedPathBuf, Tag};
use crate::PackageManager;
use ring_traits::{DependencyIterator, Project};
use ring_utils::{Dependency, NormalizedPath, NormalizedPathBuf, Tag, Tagged};
use semver::Version;
use std::rc::Rc;
use crate::PackageManager;
use crate::utils::parse_js_requirement;

#[derive(Debug)]
pub struct JsProject {
Expand Down Expand Up @@ -39,10 +40,29 @@ impl Project for JsProject {
fn version(&self) -> Option<&Version> {
self.manifest.version.as_ref()
}

fn dependencies(&self) -> Box<DependencyIterator> {
let deps = self.manifest.dependencies.iter()
.map(|(name, req)| parse_js_requirement(req, &self.root)
.map(|req| Dependency::new(name.to_string(), req))
);

let dev_deps = self.manifest.dev_dependencies.iter()
.map(|(name, req)| parse_js_requirement(req, &self.root)
.map(|req| Dependency::new(name.to_string(), req).with_tag(dev_tag()))
);

let opt_deps = self.manifest.optional_dependencies.iter()
.map(|(name, req)| parse_js_requirement(req, &self.root)
.map(|req| Dependency::new(name.to_string(), req).with_tag(optional_tag()))
);

Box::new(deps.chain(dev_deps).chain(opt_deps))
}
}

impl Tagged for JsProject {
fn tags(&self) -> &[&'static Tag] {
&[&JS_TAG]
fn tags(&self) -> Vec<Tag> {
vec![js_tag()]
}
}
8 changes: 4 additions & 4 deletions crates/ring-js/src/project_detector.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
use crate::constants::MANIFEST;
use crate::lockfile_detector::JsLockfileDetector;
use crate::{JsProject, PackageManifest};
use ring_files::ManifestLoader;
use ring_traits::{Detect, DetectAs, Project, Tagged, detect_as, detect_from};
use ring_traits::{detect_as, detect_from, Detect, DetectAs, Project};
use ring_utils::OptionalResult::{self, Found};
use ring_utils::{NormalizedPath, PathTree};
use ring_utils::{NormalizedPath, PathTree, Tagged};
use std::cell::RefCell;
use std::rc::Rc;
use tracing::{debug, info};
use crate::lockfile_detector::JsLockfileDetector;

#[derive(Debug)]
pub struct JsProjectDetector {
Expand Down Expand Up @@ -62,5 +62,5 @@ impl Detect for JsProjectDetector {
}
}

detect_as!(JsProjectDetector, Rc<dyn Project>);
detect_as!(JsProjectDetector, Rc<dyn Tagged>);
detect_as!(JsProjectDetector, Rc<dyn Project>);
10 changes: 5 additions & 5 deletions crates/ring-js/src/scope.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use crate::constants::JS_TAG;
use crate::constants::js_tag;
use crate::{JsProject, JsProjectDetector, PackageManager};
use ring_files::PatternIterator;
use ring_traits::{Project, ProjectIterator, Scope, Tagged};
use ring_utils::{NormalizedPath, Tag};
use ring_traits::{Project, ProjectIterator, Scope};
use ring_utils::{NormalizedPath, Tag, Tagged};
use std::rc::Rc;
use tracing::{debug, warn};

Expand Down Expand Up @@ -50,7 +50,7 @@ impl Scope for JsScope {
}

impl Tagged for JsScope {
fn tags(&self) -> &[&'static Tag] {
&[&JS_TAG]
fn tags(&self) -> Vec<Tag> {
vec![js_tag()]
}
}
6 changes: 3 additions & 3 deletions crates/ring-js/src/scope_detector.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
use std::cell::RefCell;
use crate::{JsProjectDetector, JsScope};
use ring_traits::{Detect, DetectAs, Scope, Tagged, detect_as, detect_from};
use ring_traits::{detect_as, detect_from, Detect, DetectAs, Scope};
use ring_utils::OptionalResult::{self, Found};
use ring_utils::{NormalizedPath, PathTree, Tagged};
use std::cell::RefCell;
use std::rc::Rc;
use tracing::{debug, info};
use ring_utils::{NormalizedPath, PathTree};

#[derive(Debug)]
pub struct JsScopeDetector {
Expand Down
38 changes: 38 additions & 0 deletions crates/ring-js/src/utils.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
use semver::VersionReq;
use tracing::warn;
use ring_utils::{NormalizedPath, Requirement};

pub fn parse_js_requirement(requirement: &str, base_path: &NormalizedPath) -> anyhow::Result<Requirement> {
Ok(match requirement.split_once(':') {
None if requirement == "*" => Requirement::Any,
None => Requirement::Version(VersionReq::parse(requirement)?),
Some(("file", path)) => Requirement::Path(base_path.join(path)),
_ => {
warn!("Unknown js requirement: {}", requirement);
Requirement::default()
}
})
}

#[cfg(test)]
mod tests {
use std::path::Path;
use ring_utils::Normalize;
use super::*;

#[test]
fn it_should_return_parsed_requirement() {
let path = Path::new("/test/foo").normalize();

assert_eq!(parse_js_requirement("*", &path).unwrap(), Requirement::Any);
assert_eq!(parse_js_requirement("^1.0.0", &path).unwrap(), Requirement::Version(VersionReq::parse("^1.0.0").unwrap()));
assert_eq!(parse_js_requirement("file:../bar", &path).unwrap(), Requirement::Path(Path::new("/test/bar").normalize()));
}

#[test]
fn it_should_return_default_for_unsupported_protocol() {
let path = Path::new("/test/foo").normalize();

assert_eq!(parse_js_requirement("toto:foo", &path).unwrap(), Requirement::default());
}
}
4 changes: 3 additions & 1 deletion crates/ring-rust/src/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,6 @@ use ring_utils::Tag;

pub const MANIFEST: &str = "Cargo.toml";

pub const RUST_TAG: Tag = Tag::with_color("rust", Rgb(227, 59, 38));
pub fn rust_tag() -> Tag {
Tag::from("rust").with_color(Rgb(227, 59, 38))
}
Loading
Loading