use std::collections::BTreeMap;
use std::path::Path;
use std::{fs, io};
use askalono::{Store, TextData};
use crate::generator::go::GoProvider;
use crate::generator::python::PythonProvider;
use crate::generator::rust::RustProvider;
use crate::snap::{App, File, Part};
use crate::Result;
mod go;
mod python;
mod rust;
static LICENSE_CACHE: &[u8] = include_bytes!("embedded-cache.bin.zstd");
#[derive(PartialEq, Clone)]
pub enum Version {
Git,
Auto,
Fixed(String),
}
impl From<&str> for Version {
fn from(s: &str) -> Self {
match s {
"git" => Version::Git,
"auto" => Version::Auto,
version => Version::Fixed(version.to_string()),
}
}
}
#[derive(Clone)]
pub struct Options {
pub snap_version: Version,
pub source_name: String,
}
trait Provider<G: Generator> {
fn provide<P: AsRef<Path>>(source_path: P, source_name: &str) -> Result<G>;
fn can_provide<P: AsRef<Path>>(source_path: P) -> bool;
}
pub trait Generator {
fn name(&self) -> Result<Option<String>>;
fn version(&self) -> Result<Option<String>>;
fn summary(&self) -> Result<Option<String>>;
fn description(&self) -> Result<Option<String>>;
fn license(&self) -> Result<Option<String>>;
fn parts(&self) -> Result<BTreeMap<String, Part>>;
fn apps(&self) -> Result<BTreeMap<String, App>>;
}
pub enum Generators {
Go(GoProvider),
Rust(RustProvider),
Python(PythonProvider),
}
impl Generators {
fn find_generator<P: AsRef<Path>>(
source_path: P,
source_name: &str,
) -> Result<Box<dyn Generator>> {
if RustProvider::can_provide(&source_path) {
log::debug!("Using RustGenerator");
let provider = RustProvider::provide(&source_path, source_name);
match provider {
Ok(v) => Ok(Box::new(v)),
Err(e) => Err(e),
}
} else if GoProvider::can_provide(&source_path) {
log::debug!("Using GoGenerator");
let provider = GoProvider::provide(&source_path, source_name);
match provider {
Ok(v) => Ok(Box::new(v)),
Err(e) => Err(e),
}
} else if PythonProvider::can_provide(&source_path) {
log::debug!("Using PythonGenerator");
let provider = PythonProvider::provide(&source_path, source_name);
match provider {
Ok(v) => Ok(Box::new(v)),
Err(e) => Err(e),
}
} else {
Err("Cannot find corresponding generator.".into())
}
}
pub fn generate<P: AsRef<Path>>(source_path: P, options: &Options) -> Result<File> {
let generator = Generators::find_generator(&source_path, &options.source_name)?;
let mut snap = File::new(&options.source_name);
match &options.snap_version {
Version::Git => snap.version = "git".to_string(),
Version::Fixed(version) => {
log::debug!("Set snap version to {}", version);
snap.version = version.clone()
}
_ => {}
}
if let Some((license, filename)) = find_license(&source_path)? {
let store = Store::from_cache(LICENSE_CACHE)?;
let result = store.analyze(&TextData::from(license));
if result.score > 0.9 {
snap.license = result.name.to_string();
log::debug!(
"Auto-detect snap license ({}) from file {}",
result.name,
filename
);
}
}
if let Some(name) = generator.name()? {
log::debug!("Set snap name to `{}`", name);
snap.name = name;
}
if options.snap_version == Version::Auto {
if let Some(version) = generator.version()? {
log::debug!("Set snap version to `{}`", version);
snap.version = version;
}
}
if let Some(summary) = generator.summary()? {
log::debug!("Set snap summary to `{}`", summary);
snap.summary = summary;
}
if let Some(description) = generator.description()? {
log::debug!("Set snap description to `{}`", description);
snap.description = description;
}
if let Some(license) = generator.license()? {
log::debug!("Set snap license to `{}`", license);
snap.license = license;
}
let parts = generator.parts()?;
if parts.is_empty() {
return Err("No parts found.".into());
}
snap.parts = parts;
let apps = generator.apps()?;
if apps.is_empty() {
return Err("No apps found.".into());
}
snap.apps = apps;
Ok(snap)
}
}
fn find_license<P: AsRef<Path>>(source_path: P) -> io::Result<Option<(String, String)>> {
if source_path.as_ref().join("LICENSE").exists() {
fs::read_to_string(source_path.as_ref().join("LICENSE"))
.map(|v| Some((v, "LICENSE".to_string())))
} else if source_path.as_ref().join("LICENSE.md").exists() {
fs::read_to_string(source_path.as_ref().join("LICENSE.md"))
.map(|v| Some((v, "LICENSE.md".to_string())))
} else if source_path.as_ref().join("COPYING").exists() {
fs::read_to_string(source_path.as_ref().join("COPYING"))
.map(|v| Some((v, "COPYING".to_string())))
} else {
Ok(None)
}
}