feat: add package identifiers
This commit is contained in:
parent
6e30a8bed7
commit
a0416cf6a9
5 changed files with 185 additions and 1 deletions
|
@ -6,4 +6,8 @@ edition = "2021"
|
|||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
fern = "0.6.2"
|
||||
getopts = "0.2.21"
|
||||
log = "0.4.19"
|
||||
regex = "1.9.1"
|
||||
manifest = { path = "../manifest" }
|
8
pkgr/src/commands/mod.rs
Normal file
8
pkgr/src/commands/mod.rs
Normal file
|
@ -0,0 +1,8 @@
|
|||
use crate::package::identifier::PackageIdentifier;
|
||||
|
||||
pub enum Command {
|
||||
Grab(PackageIdentifier), // grab a package from a remote source
|
||||
Remove(PackageIdentifier), // remove a package from the local source
|
||||
List, // list all packages in the local source
|
||||
Update,
|
||||
}
|
|
@ -1,2 +1,59 @@
|
|||
use std::env;
|
||||
use std::process::exit;
|
||||
use std::str::FromStr;
|
||||
|
||||
use getopts::Options;
|
||||
use log::{info, SetLoggerError};
|
||||
|
||||
mod commands;
|
||||
mod package;
|
||||
|
||||
fn main() {
|
||||
setup_logger()
|
||||
.expect("Unable to setup logger.");
|
||||
|
||||
let args: Vec<String> = env::args().collect();
|
||||
let program = args[0].clone();
|
||||
|
||||
let mut opts = Options::new();
|
||||
opts.optflag("h", "help", "Show help message.");
|
||||
let matches = opts.parse(&args[1..]).unwrap_or_else(|e| panic!("{}", e.to_string()));
|
||||
|
||||
if matches.opt_present("h") {
|
||||
print_usage(&program, opts);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
let command = if !matches.free.is_empty() {
|
||||
matches.free[0].clone()
|
||||
} else {
|
||||
print_usage(&program, opts);
|
||||
exit(1);
|
||||
};
|
||||
|
||||
info!("Identifier: {}", package::identifier::PackageIdentifier::from_str(&command).unwrap());
|
||||
}
|
||||
|
||||
fn print_usage(program: &str, opts: Options) {
|
||||
let mut brief = format!("Usage: {} <COMMAND> [options]\n\n", program);
|
||||
brief += &*format!("Commands: grab \n");
|
||||
brief += &*format!(" remove\n");
|
||||
print!("{}", opts.usage(&brief.trim_end()));
|
||||
}
|
||||
|
||||
fn setup_logger() -> Result<(), SetLoggerError> {
|
||||
fern::Dispatch::new()
|
||||
.format(|out, message, record| {
|
||||
out.finish(format_args!(
|
||||
"{} {}",
|
||||
match record.level().to_string().chars().nth(0).unwrap_or('I') {
|
||||
'E' | 'W' => "!!",
|
||||
_ => "**",
|
||||
},
|
||||
message
|
||||
))
|
||||
})
|
||||
.level(log::LevelFilter::Info)
|
||||
.chain(std::io::stdout())
|
||||
.apply()
|
||||
}
|
114
pkgr/src/package/identifier.rs
Normal file
114
pkgr/src/package/identifier.rs
Normal file
|
@ -0,0 +1,114 @@
|
|||
use std::str::FromStr;
|
||||
|
||||
use regex::Regex;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum PackageIdentifierError {
|
||||
InvalidPackageLocator(String),
|
||||
InvalidURI(String),
|
||||
}
|
||||
|
||||
impl std::fmt::Display for PackageIdentifierError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
match self {
|
||||
PackageIdentifierError::InvalidPackageLocator(s) => write!(f, "Invalid package locator: {}", s),
|
||||
PackageIdentifierError::InvalidURI(s) => write!(f, "Invalid URI: {}", s),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum PackageIdentifier {
|
||||
PackageLocator(PackageLocator),
|
||||
URI(String),
|
||||
}
|
||||
|
||||
impl std::fmt::Display for PackageIdentifier {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
match self {
|
||||
PackageIdentifier::PackageLocator(pl) => write!(f, "{}", pl),
|
||||
PackageIdentifier::URI(uri) => write!(f, "{}", uri),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct PackageLocator {
|
||||
pub name: String,
|
||||
pub version: Option<u32>,
|
||||
pub tags: Option<Vec<String>>,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for PackageLocator {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
let mut s = self.name.clone();
|
||||
if let Some(version) = self.version {
|
||||
s += &*format!("@{}", version);
|
||||
}
|
||||
if let Some(tags) = &self.tags {
|
||||
s += &*format!(":{}", tags.join(","));
|
||||
}
|
||||
write!(f, "{}", s)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for PackageIdentifier {
|
||||
type Err = PackageIdentifierError;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let uri_re = regex::Regex::new(r"^[a-zA-Z0-9]+://").unwrap();
|
||||
if uri_re.is_match(s) {
|
||||
// there needs to be stuff after the protocol
|
||||
let split = s.split("://").collect::<Vec<&str>>();
|
||||
if split.len() != 2 || split[1].is_empty() {
|
||||
return Err(PackageIdentifierError::InvalidURI(s.to_string()));
|
||||
}
|
||||
Ok(PackageIdentifier::URI(s.to_string()))
|
||||
} else {
|
||||
let pl = match PackageLocator::from_str(s) {
|
||||
Ok(pl) => pl,
|
||||
Err(e) => return Err(PackageIdentifierError::InvalidPackageLocator(e.to_string())),
|
||||
};
|
||||
Ok(PackageIdentifier::PackageLocator(pl))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for PackageLocator {
|
||||
type Err = PackageIdentifierError;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
#[allow(unused_assignments)] // false positive
|
||||
let mut name = None;
|
||||
let mut version = None;
|
||||
let mut tags = None;
|
||||
|
||||
if s.is_empty() {
|
||||
return Err(PackageIdentifierError::InvalidPackageLocator(s.to_string()));
|
||||
}
|
||||
|
||||
let name_re = Regex::new("^([A-Za-z][A-Za-z0-9_.]+)").unwrap();
|
||||
let version_re = Regex::new("@([0-9]+)").unwrap();
|
||||
let tags_re = Regex::new(":([a-zA-Z0-9,._]+)").unwrap();
|
||||
|
||||
if let Some(caps) = name_re.captures(s) {
|
||||
name = Some(caps.get(1).unwrap().as_str().to_string());
|
||||
} else {
|
||||
return Err(PackageIdentifierError::InvalidPackageLocator(s.to_string()));
|
||||
}
|
||||
|
||||
if let Some(caps) = version_re.captures(s) {
|
||||
version = Some(caps.get(1).unwrap().as_str().parse::<u32>().unwrap());
|
||||
}
|
||||
|
||||
if let Some(caps) = tags_re.captures(s) {
|
||||
tags = Some(caps.get(1).unwrap().as_str().split(",").map(|s| s.to_string()).collect());
|
||||
}
|
||||
|
||||
Ok(PackageLocator {
|
||||
name: name.unwrap(),
|
||||
version,
|
||||
tags,
|
||||
})
|
||||
}
|
||||
}
|
1
pkgr/src/package/mod.rs
Normal file
1
pkgr/src/package/mod.rs
Normal file
|
@ -0,0 +1 @@
|
|||
pub mod identifier;
|
Loading…
Reference in a new issue