Skip to content

Commit

Permalink
comment and API docs:
Browse files Browse the repository at this point in the history
- session
- catalog
- manifest
- style
- tool

move Index to catalog module since that's the only place it's used

more uniform variable and method names:
- VersionReq variables: `req` -> `requirements` or `matching`
- `set_node_version` methods to `activate_node`
- `install_node_req` methods to `install_node`
  • Loading branch information
dherman committed Feb 19, 2018
1 parent 54b8c46 commit a4c4a31
Show file tree
Hide file tree
Showing 11 changed files with 87 additions and 47 deletions.
34 changes: 21 additions & 13 deletions crates/notion-core/src/catalog.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//! Provides types for working with Notion's local _catalog_, the local repository
//! of available tool versions.

use std::collections::BTreeSet;
use std::collections::{HashSet, BTreeSet, BTreeMap};
use std::fs::{File, remove_dir_all};
use std::io::{self, Write};
use std::str::FromStr;
Expand Down Expand Up @@ -73,8 +73,8 @@ impl Catalog {
Ok(())
}

pub fn set_node_version(&mut self, req: &VersionReq, config: &Config) -> Result<(), failure::Error> {
let installed = self.install_node_req(req, config)?;
pub fn activate_node(&mut self, matching: &VersionReq, config: &Config) -> Result<(), failure::Error> {
let installed = self.install_node(matching, config)?;
let version = Some(installed.into_version());

if self.node.current != version {
Expand All @@ -85,8 +85,8 @@ impl Catalog {
Ok(())
}

pub fn install_node_req(&mut self, req: &VersionReq, config: &Config) -> Result<Installed, failure::Error> {
let installer = self.node.resolve_remote(&req, config)?;
pub fn install_node(&mut self, matching: &VersionReq, config: &Config) -> Result<Installed, failure::Error> {
let installer = self.node.resolve_remote(&matching, config)?;
let installed = installer.install(&self.node)?;

if let &Installed::Now(ref version) = &installed {
Expand Down Expand Up @@ -120,9 +120,9 @@ impl Catalog {
}

#[derive(Fail, Debug)]
#[fail(display = "No Node version found for {}", req)]
#[fail(display = "No Node version found for {}", matching)]
struct NoNodeVersionFoundError {
req: VersionReq
matching: VersionReq
}

impl NodeCatalog {
Expand All @@ -131,30 +131,30 @@ impl NodeCatalog {
self.versions.contains(version)
}

fn resolve_remote(&self, req: &VersionReq, config: &Config) -> Result<Installer, failure::Error> {
fn resolve_remote(&self, matching: &VersionReq, config: &Config) -> Result<Installer, failure::Error> {
match config.node {
Some(NodeConfig { resolve: Some(ref plugin), .. }) => {
plugin.resolve(req)
plugin.resolve(matching)
}
_ => {
self.resolve_public(req)
self.resolve_public(matching)
}
}
}

fn resolve_public(&self, req: &VersionReq) -> Result<Installer, failure::Error> {
fn resolve_public(&self, matching: &VersionReq) -> Result<Installer, failure::Error> {
let serial: serial::index::Index = reqwest::get(PUBLIC_NODE_VERSION_INDEX)?.json()?;
let index = serial.into_index()?;
let version = index.entries.iter()
.rev()
// ISSUE #34: also make sure this OS is available for this version
.skip_while(|&(ref k, _)| !req.matches(k))
.skip_while(|&(ref k, _)| !matching.matches(k))
.next()
.map(|(k, _)| k.clone());
if let Some(version) = version {
Installer::public(version)
} else {
Err(NoNodeVersionFoundError { req: req.clone() }.into())
Err(NoNodeVersionFoundError { matching: matching.clone() }.into())
}
}

Expand All @@ -169,6 +169,14 @@ impl NodeCatalog {

}

pub struct Index {
pub entries: BTreeMap<Version, VersionData>
}

pub struct VersionData {
pub files: HashSet<String>
}

impl FromStr for Catalog {
type Err = failure::Error;

Expand Down
7 changes: 7 additions & 0 deletions crates/notion-core/src/manifest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,23 @@ use failure;

use serial;

/// A Node manifest file.
pub struct Manifest {
/// The requested version of Node, under the `notion.node` key.
pub node: VersionReq,
/// The requested version of Yarn, under the `notion.yarn` key.
pub yarn: Option<VersionReq>,
/// The `dependencies` section.
pub dependencies: HashMap<String, String>
}

impl Manifest {

/// Loads and parses a Node manifest for the project rooted at the specified path.
pub fn for_dir(project_root: &Path) -> Result<Option<Manifest>, failure::Error> {
let file = File::open(project_root.join("package.json"))?;
let serial: serial::manifest::Manifest = serde_json::de::from_reader(file)?;
serial.into_manifest()
}

}
2 changes: 1 addition & 1 deletion crates/notion-core/src/plugin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ pub struct InvalidCommandError {
}

impl Resolve {
pub fn resolve(&self, _req: &VersionReq) -> Result<Installer, failure::Error> {
pub fn resolve(&self, _matching: &VersionReq) -> Result<Installer, failure::Error> {
match self {
&Resolve::Url(_) => {
unimplemented!()
Expand Down
8 changes: 4 additions & 4 deletions crates/notion-core/src/serial/index.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use super::super::session;
use super::super::catalog;

use std::collections::{HashSet, BTreeMap};
use std::iter::FromIterator;
Expand All @@ -16,10 +16,10 @@ pub struct Entry {
}

impl Index {
pub fn into_index(self) -> Result<session::Index, failure::Error> {
pub fn into_index(self) -> Result<catalog::Index, failure::Error> {
let mut entries = BTreeMap::new();
for entry in self.0 {
let data = session::VersionData {
let data = catalog::VersionData {
files: HashSet::from_iter(entry.files.into_iter())
};
let mut version = &entry.version[..];
Expand All @@ -29,6 +29,6 @@ impl Index {
}
entries.insert(Version::parse(version)?, data);
}
Ok(session::Index { entries })
Ok(catalog::Index { entries })
}
}
6 changes: 3 additions & 3 deletions crates/notion-core/src/serial/manifest.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use super::super::manifest;
use super::version::parse_req;
use super::version::parse_requirements;

use failure;

Expand Down Expand Up @@ -32,9 +32,9 @@ impl Manifest {
pub fn into_manifest(self) -> Result<Option<manifest::Manifest>, failure::Error> {
if let Some(notion) = self.notion {
return Ok(Some(manifest::Manifest {
node: parse_req(&notion.node)?,
node: parse_requirements(&notion.node)?,
yarn: if let Some(yarn) = notion.yarn {
Some(parse_req(&yarn)?)
Some(parse_requirements(&yarn)?)
} else {
None
},
Expand Down
2 changes: 1 addition & 1 deletion crates/notion-core/src/serial/version.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use semver::VersionReq;
use failure;

pub fn parse_req(src: &str) -> Result<VersionReq, failure::Error> {
pub fn parse_requirements(src: &str) -> Result<VersionReq, failure::Error> {
let src = src.trim();
Ok(if src.len() > 0 && src.chars().next().unwrap().is_digit(10) {
let defaulted = format!("={}", src);
Expand Down
47 changes: 29 additions & 18 deletions crates/notion-core/src/session.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,13 @@ use installer::Installed;
use failure;
use semver::{Version, VersionReq};

use std::collections::{HashSet, BTreeMap};

/// Represents the user's state during an execution of a Notion tool. The session
/// encapsulates a number of aspects of the environment in which the tool was
/// invoked, including:
/// - the current directory
/// - the Node project tree that contains the current directory (if any)
/// - the Notion configuration settings
/// - the catalog of locally-installed Notion tools
pub struct Session {
config: LazyConfig,
catalog: LazyCatalog,
Expand All @@ -20,6 +25,7 @@ pub struct Session {

impl Session {

/// Constructs a new `Session`.
pub fn new() -> Result<Session, failure::Error> {
Ok(Session {
config: LazyConfig::new(),
Expand All @@ -28,58 +34,63 @@ impl Session {
})
}

/// Produces a reference to the current Node project, if any.
pub fn project(&self) -> Option<&Project> {
self.project.as_ref()
}

/// Produces a reference to the current tool catalog.
pub fn catalog(&self) -> Result<&Catalog, failure::Error> {
self.catalog.get()
}

/// Produces a mutable reference to the current tool catalog.
pub fn catalog_mut(&mut self) -> Result<&mut Catalog, failure::Error> {
self.catalog.get_mut()
}

/// Produces a reference to the configuration.
pub fn config(&self) -> Result<&Config, failure::Error> {
self.config.get()
}

pub fn node(&mut self) -> Result<Option<Version>, failure::Error> {
/// Produces the version of Node for the current session. If there is an
/// active project with Notion settings, this will ensure a compatible
/// version of Node is installed before returning. If there is no active
/// project with Notion settings, this produces the global version, which
/// may be `None`.
pub fn current_node(&mut self) -> Result<Option<Version>, failure::Error> {
if let Some(ref project) = self.project {
let req = &project.manifest().node;
let requirements = &project.manifest().node;
let catalog = self.catalog.get_mut()?;
let available = catalog.node.resolve_local(&req);
let available = catalog.node.resolve_local(&requirements);

if available.is_some() {
return Ok(available);
}

let config = self.config.get()?;
let installed = catalog.install_node_req(&req, config)?;
let installed = catalog.install_node(&requirements, config)?;

return Ok(Some(installed.into_version()));
}

Ok(self.catalog()?.node.current.clone())
}

pub fn install_node(&mut self, req: &VersionReq) -> Result<Installed, failure::Error> {
/// Installs a version of Node matching the specified semantic verisoning
/// requirements.
pub fn install_node(&mut self, matching: &VersionReq) -> Result<Installed, failure::Error> {
let catalog = self.catalog.get_mut()?;
let config = self.config.get()?;
catalog.install_node_req(req, config)
catalog.install_node(matching, config)
}

pub fn set_node_version(&mut self, req: &VersionReq) -> Result<(), failure::Error> {
/// Activates a version of Node matching the specified semantic versioning
/// requirements.
pub fn activate_node(&mut self, matching: &VersionReq) -> Result<(), failure::Error> {
let catalog = self.catalog.get_mut()?;
let config = self.config.get()?;
catalog.set_node_version(req, config)
catalog.activate_node(matching, config)
}
}

pub struct Index {
pub entries: BTreeMap<Version, VersionData>
}

pub struct VersionData {
pub files: HashSet<String>
}
8 changes: 7 additions & 1 deletion crates/notion-core/src/style.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,28 @@ use failure;
use indicatif::{ProgressBar, ProgressStyle};
use term_size;

/// Displays an error to stderr.
pub fn display_error<E: Into<failure::Error>>(err: E) {
display_error_prefix();
eprintln!("{}", err.into());
}

/// Displays an error to stderr with a styled `"error:"` prefix.
pub fn display_error_prefix() {
eprint!("{} ", style("error:").red().bold());
}

/// Constructs a command-line progress bar with the specified "action" string
/// (e.g., `"Installing"`), details string (e.g., `"v1.23.4"`), and logical
/// length (i.e., the number of logical progress steps in the process being
/// visualized by the progress bar).
pub fn progress_bar(action: &str, details: &str, len: u64) -> ProgressBar {
let display_width = term_size::dimensions().map(|(w, _)| w).unwrap_or(80);
let msg_width = 12 + 1 + details.len();

// Installing v1.23.4 [====================> ] 50%
// |----------| |-----| |--------------------------------------| |-|
// msg bar percentage
// action details bar percentage
let available_width = display_width - 2 - msg_width - 2 - 2 - 1 - 3 - 1;
let bar_width = ::std::cmp::min(available_width, 40);

Expand Down
10 changes: 9 additions & 1 deletion crates/notion-core/src/tool.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use env;
use failure;
use style;

/// Represents a command-line tool that Notion shims delegate to.
pub trait Tool: Sized {
fn launch() -> ! {
match Self::new() {
Expand All @@ -22,12 +23,16 @@ pub trait Tool: Sized {
}
}

/// Constructs a new instance.
fn new() -> Result<Self, failure::Error>;

/// Constructs a new instance, using the specified command-line and `PATH` variable.
fn from_components(exe: &OsStr, args: ArgsOs, path_var: &OsStr) -> Self;

/// Extracts the `Command` from this tool.
fn command(self) -> Command;

/// Delegates the current process to this tool.
fn exec(self) -> ! {
let mut command = self.command();
let status = command.status();
Expand All @@ -47,10 +52,13 @@ pub trait Tool: Sized {
}
}

/// Represents a delegated script.
pub struct Script(Command);

/// Represents a delegated binary executable.
pub struct Binary(Command);

/// Represents a Node executable.
pub struct Node(Command);

#[cfg(windows)]
Expand Down Expand Up @@ -134,7 +142,7 @@ impl Tool for Node {
let mut session = Session::new()?;
let mut args = args_os();
let exe = arg0(&mut args)?;
let version = if let Some(version) = session.node()? {
let version = if let Some(version) = session.current_node()? {
version
} else {
return Err(NoGlobalError.into());
Expand Down
6 changes: 3 additions & 3 deletions src/command/activate.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use docopt::Docopt;
use notion_core::session::Session;
use notion_core::serial::version::parse_req;
use notion_core::serial::version::parse_requirements;
use std::process::exit;
use failure;

Expand Down Expand Up @@ -31,9 +31,9 @@ pub fn run(mut args: Vec<String>, _verbose: bool) -> Result<(), failure::Error>

if args.flag_global {
let version = args.arg_version;
let req = parse_req(&version)?;
let requirements = parse_requirements(&version)?;
let mut session = Session::new()?;
session.set_node_version(&req)?;
session.activate_node(&requirements)?;
} else {
println!("not yet implemented; in the meantime you can modify your package.json.");
exit(1);
Expand Down

0 comments on commit a4c4a31

Please sign in to comment.