use std::error;
use std::fs::{read_to_string,create_dir_all};
use std::path::Path;
use std::path::PathBuf;
use log::{info};
use reqwest::Url;
use crate::{init_classpath};
use crate::util;
use crate::config::{Config,Key,Error};
use crate::jvm::{Jvm};
use crate::package::{Dependency, PackageResolver};
use crate::platform;
use crate::platform::{Instance,JavaInstance};
pub static PACKAGE_NAME : Key = Key::new(&["package","name"]);
pub static PACKAGE_AUTHORS : Key = Key::new(&["package","authors"]);
pub static PACKAGE_VERSION : Key = Key::new(&["package","version"]);
pub static BUILD_PLATFORMS : Key = Key::new(&["build","platforms"]);
pub static DEPENDENCIES : Key = Key::new(&["dependencies"]);
const PACKAGE_CENTRAL : &str = "https://github.com/Whiley/Repository/raw/master/";
#[derive(Debug)]
pub enum Kind {
Warning,
SyntaxError,
InternalFailure
}
#[derive(Debug)]
pub struct Marker {
kind: Kind,
path: PathBuf,
start: usize,
end: usize,
message: String
}
impl Marker {
pub fn new(kind: Kind, path: PathBuf, start: usize, end: usize, message: String) -> Self {
Marker{kind,path,start,end,message}
}
pub fn enclosing_line(&self) -> Result<Line,Box<dyn error::Error>> {
let contents = read_to_string(self.path.as_path())?;
let mut line = 1;
for l in util::line_offsets(contents.as_str()) {
if l.contains(self.start) {
return Ok(Line{offset:l.start,line,contents:l.as_str().to_string()});
}
line = line + 1;
}
panic!("No enclosing line!");
}
}
pub struct Line {
pub offset: usize,
pub line: usize,
pub contents: String
}
pub struct Build {
pub name: String,
pub authors: Vec<String>,
pub version: String,
pub platforms: Vec<platform::Instance>,
pub dependencies: Vec<Dependency>
}
impl Build {
pub fn from_str<'a>(config: &Config, whileyhome: &Path, registry: &'a platform::Registry<'a>) -> Result<Build,Error> {
let name = config.get_string(&PACKAGE_NAME)?;
let authors = config.get_string_array(&PACKAGE_AUTHORS)?;
let version = config.get_string(&PACKAGE_VERSION)?;
let platforms = config.get_string_array(&BUILD_PLATFORMS)?;
let deps = config.get_strings(&DEPENDENCIES).unwrap_or(Vec::new());
let mut ps = Vec::new();
for p in &platforms {
let init = match registry.get(p) {
None => {
return Err(Error::UnknownPlatform(p.to_string()));
}
Some(v) => v
};
ps.push(init.apply(config,whileyhome)?);
}
let dependencies = deps.into_iter().map(|(k,v)| Dependency::new(k,v)).collect();
return Ok(Build{name,authors,version,platforms:ps,dependencies});
}
pub fn manifest(&self) -> Manifest {
Manifest::new(self)
}
pub fn run(&self, whileyhome: &Path) -> Result<bool,Box<dyn error::Error>> {
self.initialise(whileyhome)?;
for p in &self.platforms {
let result = match p {
Instance::Java(i) => {
self.run_java(i.as_ref(),whileyhome)
},
Instance::Rust(_) => {
todo!("Rust platforms not currently supported")
}
};
match result {
Ok(markers) => {
if markers.len() > 0 {
for m in markers {
let l = m.enclosing_line()?;
let f = m.path.into_os_string().into_string().unwrap();
println!("{}:{}:{}",f,l.line,m.message);
println!("{}",l.contents);
let padding = " ".repeat(m.start - l.offset);
let highlight = "^".repeat(m.end - m.start + 1);
println!("{}{}",padding,highlight);
}
return Ok(false);
}
}
Err(out) => {
println!("{}",out);
return Ok(false);
}
}
}
Ok(true)
}
fn run_java(&self, i: &dyn JavaInstance, whileyhome: &Path) -> Result<Vec<Marker>,Box<dyn error::Error>> {
let cp = init_classpath(&whileyhome,i.dependencies())?;
let jvm = Jvm::new(cp,vec![("WHILEYHOME",&whileyhome)]);
let args : Vec<String> = i.arguments();
let str_args : Vec<&str> = args.iter().map(String::as_str).collect();
info!("Executing java {:?}",str_args);
let output = jvm.exec(&str_args);
info!("Java output \"{}\"",output.as_str());
i.process(output.as_str())
}
fn initialise(&self, whileyhome: &Path) -> Result<(),Box<dyn error::Error>> {
self.create_binary_folders()?;
self.resolve_packages(whileyhome)?;
Ok(())
}
fn create_binary_folders(&self) -> Result<(),Box<dyn error::Error>> {
for ba in self.manifest() {
match ba {
Artifact::BinaryFolder(p) => {
if !p.as_path().exists() {
info!("Making binary folder {}",p.display());
create_dir_all(p)?;
}
}
_ => {
}
};
}
Ok(())
}
fn resolve_packages(&self, whileyhome: &Path) -> Result<(),Box<dyn error::Error>> {
let mut repo = PathBuf::from(whileyhome);
repo.push("repository");
let base_url = Url::parse(PACKAGE_CENTRAL).unwrap();
let resolver = PackageResolver::new(repo, base_url);
resolver.resolve(&self.dependencies)?;
Ok(())
}
}
#[derive(Debug)]
pub enum Artifact {
SourceFile(PathBuf),
SourceFolder(PathBuf),
BinaryFile(PathBuf, bool),
BinaryFolder(PathBuf)
}
pub struct Manifest {
artifacts: Vec<Artifact>
}
impl Manifest {
pub fn new(b: &Build) -> Manifest {
let mut artifacts = Vec::new();
artifacts.push(Artifact::SourceFile(PathBuf::from("wy.toml")));
for i in &b.platforms {
artifacts.extend(i.manifest());
}
Manifest{artifacts}
}
}
impl IntoIterator for Manifest {
type Item = Artifact;
type IntoIter = std::vec::IntoIter<Self::Item>;
fn into_iter(self) -> Self::IntoIter {
self.artifacts.into_iter()
}
}