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

feat: Implement ls command #17

Draft
wants to merge 6 commits into
base: main
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
7 changes: 7 additions & 0 deletions Cargo.lock

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

3 changes: 0 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,9 @@ license = "MIT"
repository = "https://github.com/anonrig/pacquet"

[workspace.dependencies]
# Crates
pacquet_cli = { path = "crates/cli" }
pacquet_registry = { path = "crates/registry" }
pacquet_tarball = { path = "crates/tarball" }
pacquet_package_json = { path = "crates/package_json" }
pacquet_lockfile = { path = "crates/lockfile" }
pacquet_npmrc = { path = "crates/npmrc" }
pacquet_executor = { path = "crates/executor" }
pacquet_cafs = { path = "crates/cafs" }
Expand Down
1 change: 1 addition & 0 deletions crates/cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ node-semver = { workspace = true }
serde = { workspace = true }
serde_json = { workspace = true }
tokio = { workspace = true }
termtree = "0.4.1"

[dev-dependencies]
insta = { workspace = true }
Expand Down
114 changes: 114 additions & 0 deletions crates/cli/src/commands/list.rs
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here I'm quite stuck (sorry for the silly question, still getting used to Rust ownership concept).
I've tried several things but always stopped upon the issue of the lifetime of the strings within the loop.

cc: @anonrig

Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
use std::path::{PathBuf, MAIN_SEPARATOR};

use clap::Parser;
use termtree::Tree;

use pacquet_package_json::{DependencyGroup, PackageJson};

use crate::package_manager::{PackageManager, PackageManagerError};

#[derive(Parser, Debug)]
pub struct ListArgs {
/// Perform the command on every package subdirectories
/// or on every workspace
#[arg(long = "recursive", short = 'r')]
pub recursive: bool,
/// Log the output as JSON
#[arg(long = "json")]
pub json: bool,
/// Show the extended information
#[arg(long = "long")]
pub long: bool,
/// Outputs the package directories into a parseable format
#[arg(long = "parseable")]
pub parseable: bool,
/// List the package in the global install directory instead of the
/// current project
#[arg(long = "global", short = 'g')]
pub global: bool,
/// Max display depth of the dependency tree
#[arg(long = "depth")]
pub depth: u32,
/// Display only dependencies within dependencies or optionalDependencies
#[arg(long = "prod", short = 'p')]
pub prod: bool,
/// Display only dependencies within devDependencies
#[arg(long = "dev", short = 'd')]
pub dev: bool,
/// Omit packages from optionalDependencies
#[arg(long = "no-optional")]
pub no_opts: bool,
/// Display only depndencies that are also projects within the workspace
#[arg(long = "only-projects")]
pub projects_only: bool,
/// Display the dependencies from a given subset of dependencies
#[arg(long = "filter", short = 'f')]
pub filter: String,
}

impl ListArgs {
pub fn get_scope(&self) -> DependencyGroup {
if self.dev {
DependencyGroup::Dev
} else {
DependencyGroup::Default
}
}

pub fn get_depth(&self) -> u32 {
let mut depth: u32 = 1;
self.depth.clone_into(&mut depth);

depth
}
}

impl PackageManager {
pub fn list(
&self,
package_json: &PackageJson,
dependency_group: DependencyGroup,
node_modules_path: &PathBuf,
depth: u32,
) -> Result<Tree<&str>, PackageManagerError> {
match dependency_group {
DependencyGroup::Default => {
let mut root = Tree::new("");
let dependencies = package_json.get_dependencies(vec![DependencyGroup::Default]);

for (name, version) in dependencies {
// let label = format!("{} - {}", name, version);
// print!("{}", label);
let mut tree = helper::create_tree(name, version);

if depth > 1 {
let path = format!("{}{}{}", &name, MAIN_SEPARATOR, "package.json");
let pjson = PackageJson::from_path(&node_modules_path.join(path))?;
let subtree = self.list(
&pjson,
DependencyGroup::Default,
node_modules_path,
depth - 1,
)?;

tree.push(subtree);
}

root.push(tree.clone());
}
Comment on lines +79 to +98
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Basically, here, I'm losing the reference to the name and version, after the scope of the loop ends, which is expected.
The main issue comes at the moment of trying the build the whole tree for a single manifest.
My initial idea was to use a recursive data structure (Tree), read its dependencies, mount it into a three representation; and continue accordingly to the depth passed.

Sadly, the way I initially designed seems to not play well with the borrowed concept 🤔

Another option was to convert it to an iterator and use the utils function to map it and print it back. But wanted to keep it as last resort.

Any thoughts? 🤔


Ok(root)
}
// DependencyGroup::Dev => Ok(root),
_ => Ok(Tree::new("".clone())),
}
}
}

mod helper {
use termtree::Tree;
pub fn create_tree<'a>(name: &'a str, version: &str) -> Tree<&'a str> {
let label = format!("{}@{}", name.clone().to_owned(), version.clone().to_owned());
Tree::new(label.to_owned().as_str())
}
}
6 changes: 5 additions & 1 deletion crates/cli/src/commands/mod.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
pub mod add;
pub mod install;
pub mod list;
pub mod run;
pub mod store;

use std::{env, ffi::OsString, path::PathBuf};

use crate::commands::{
add::AddCommandArgs, install::InstallCommandArgs, run::RunCommandArgs, store::StoreSubcommands,
add::AddCommandArgs, install::InstallCommandArgs, list::ListArgs, run::RunCommandArgs,
store::StoreSubcommands,
};
use clap::{Parser, Subcommand};

Expand Down Expand Up @@ -46,4 +48,6 @@ pub enum Subcommands {
/// Managing the package store.
#[clap(subcommand)]
Store(StoreSubcommands),
/// List all dependencies installed based on package.json
List(ListArgs),
}
7 changes: 7 additions & 0 deletions crates/cli/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ pub async fn run_cli() -> Result<()> {

async fn run_commands(cli: Cli) -> Result<()> {
let package_json_path = cli.current_dir.join("package.json");
let node_modules_path = cli.current_dir.join("node_modules");

match &cli.subcommand {
Subcommands::Init => {
Expand Down Expand Up @@ -97,6 +98,12 @@ async fn run_commands(cli: Cli) -> Result<()> {
}
}
}
Subcommands::List(args) => {
let group = args.get_scope();
let depth = args.get_depth();
let manager = PackageManager::new(&package_json_path)?;
manager.list(&manager.package_json, group, &node_modules_path, depth)?;
}
}

Ok(())
Expand Down