pub mod diagnostic;
pub mod stringable;
use codespan_reporting::{
files::SimpleFiles,
term::{
self,
termcolor::{ColorChoice, StandardStream},
},
};
use diagnostic::Diagnostic;
use linked_hash_set::LinkedHashSet;
use stringable::Stringable;
#[derive(Debug)]
pub struct CompilerResult<T> {
task_name: Option<Stringable>,
value: Option<T>,
status: Status,
contains_warnings: bool,
children: Vec<CompilerResult<()>>,
errors: LinkedHashSet<Diagnostic>,
warnings: LinkedHashSet<Diagnostic>,
}
#[derive(Debug, PartialEq, Clone, Copy)]
pub enum Status {
Unresolved,
Passed,
Failed,
}
impl CompilerResult<()> {
pub fn status_only<M>(task_name: M) -> Self
where
M: Into<Stringable>,
{
CompilerResult {
task_name: Some(task_name.into()),
status: Status::Unresolved,
contains_warnings: false,
children: Vec::new(),
errors: LinkedHashSet::new(),
warnings: LinkedHashSet::new(),
value: Some(()),
}
}
}
impl<T> CompilerResult<T> {
pub fn new<M>(task_name: M) -> Self
where
M: Into<Stringable>,
{
CompilerResult {
task_name: Some(task_name.into()),
value: None,
status: Status::Unresolved,
contains_warnings: false,
children: Vec::new(),
errors: LinkedHashSet::new(),
warnings: LinkedHashSet::new(),
}
}
pub fn unnamed() -> Self {
CompilerResult {
task_name: None,
value: None,
status: Status::Unresolved,
contains_warnings: false,
children: Vec::new(),
errors: LinkedHashSet::new(),
warnings: LinkedHashSet::new(),
}
}
pub fn get_status(&self) -> Status {
self.status
}
fn fail(&mut self) {
self.status = Status::Failed;
self.value = None;
}
fn pass(&mut self) {
self.status = Status::Passed;
}
pub fn require<R, T2>(&mut self, other: R) -> Option<T2>
where
R: Into<Reportable<T2>>,
{
match other.into() {
Reportable::CompilerResult(cres) => {
let (value, status_only) = cres.into_status_only();
if status_only.get_status() == Status::Failed {
self.fail();
}
if status_only.contains_warnings {
self.contains_warnings = true;
}
self.children.push(status_only);
value
}
Reportable::Value(val) => Some(val),
Reportable::Failure(message) => {
self.error(message);
None
}
}
}
pub fn assert<M>(&mut self, condition: bool, message: M)
where
M: Into<Diagnostic>,
{
if !condition {
self.fail();
self.errors.insert(message.into());
}
}
pub fn error<M>(&mut self, message: M)
where
M: Into<Diagnostic>,
{
self.fail();
self.errors.insert(message.into());
}
pub fn warning<M>(&mut self, message: M)
where
M: Into<Diagnostic>,
{
self.contains_warnings = true;
self.warnings.insert(message.into());
}
pub fn set_value(&mut self, value: T) {
if self.get_status() != Status::Failed {
self.value = Some(value);
self.pass();
}
}
pub fn with_value(mut self, value: T) -> Self {
self.set_value(value);
self
}
fn into_status_only(self) -> (Option<T>, CompilerResult<()>) {
let status = self.get_status();
let value = self.value;
let diagnostic = CompilerResult {
task_name: self.task_name,
value: Some(()),
status: status.clone(),
contains_warnings: self.contains_warnings,
children: self.children,
errors: self.errors,
warnings: self.warnings,
};
(value, diagnostic)
}
pub fn set(&mut self, other: CompilerResult<T>) {
if let Some(value) = self.require(other) {
self.set_value(value);
}
}
pub fn set_res<E>(&mut self, res: Result<T, E>)
where
E: Into<Diagnostic>,
{
match res {
Ok(value) => self.set_value(value),
Err(error) => self.error(error),
}
}
pub fn to_option(self) -> Option<T> {
self.value
}
pub fn print_dummy_files(&self) -> Result<(), codespan_reporting::files::Error> {
let files: SimpleFiles<String, String> = SimpleFiles::new();
self.print(&files)
}
pub fn print(
&self,
files: &SimpleFiles<String, String>,
) -> Result<(), codespan_reporting::files::Error> {
let writer = StandardStream::stderr(ColorChoice::Always);
let config = codespan_reporting::term::Config::default();
if let Some(name) = &self.task_name {
println!("{}", name);
}
for error in self.errors.iter() {
let diagnostic = error.print_error();
term::emit(&mut writer.lock(), &config, files, &diagnostic)?;
}
for warning in self.warnings.iter() {
let diagnostic = warning.print_warning();
term::emit(&mut writer.lock(), &config, files, &diagnostic)?;
}
for child in self.children.iter() {
child.print_helper(0, files)?;
}
if self.get_status() == Status::Failed {
println!("FAILED")
} else {
print!("SUCCEEDED");
if !self.warnings.is_empty() {
print!(" with {} warnings", self.warnings.len());
}
println!("");
}
Ok(())
}
fn print_helper(
&self,
level: usize,
files: &SimpleFiles<String, String>,
) -> Result<(), codespan_reporting::files::Error> {
let writer = StandardStream::stderr(ColorChoice::Always);
let config = codespan_reporting::term::Config::default();
let mut next_level = level;
if self.get_status() == Status::Failed || self.contains_warnings {
if let Some(name) = &self.task_name {
println!("+{} {}", "+".repeat(level), name);
next_level += 1;
}
}
for error in self.errors.iter() {
let diagnostic = error.print_error();
term::emit(&mut writer.lock(), &config, files, &diagnostic)?;
}
for warning in self.warnings.iter() {
let diagnostic = warning.print_warning();
term::emit(&mut writer.lock(), &config, files, &diagnostic)?;
}
for child in self.children.iter() {
child.print_helper(next_level, files)?;
}
Ok(())
}
}
impl<T> CompilerResult<Vec<T>> {
fn push_other(&mut self, other: CompilerResult<T>) {
if let Some(other_val) = self.require(other) {
if let Some(current_val) = &mut self.value {
current_val.push(other_val);
}
}
}
pub fn collect<I>(&mut self, iter: I)
where
I: Iterator<Item = CompilerResult<T>>,
{
for other in iter {
self.push_other(other);
}
}
}
pub enum Reportable<T> {
CompilerResult(CompilerResult<T>),
Value(T),
Failure(Diagnostic),
}
impl<T, M> From<(Option<T>, M)> for Reportable<T>
where
M: Into<Diagnostic>,
{
fn from(other: (Option<T>, M)) -> Self {
match other {
(Some(val), _) => Reportable::Value(val),
(None, message) => Reportable::Failure(message.into()),
}
}
}
impl<T, M> From<Result<T, M>> for Reportable<T>
where
M: Into<Diagnostic>,
{
fn from(other: Result<T, M>) -> Self {
match other {
Ok(val) => Reportable::Value(val),
Err(message) => Reportable::Failure(message.into()),
}
}
}
impl<T, M, N> From<(Result<T, N>, M)> for Reportable<T>
where
M: Into<Diagnostic>,
{
fn from(other: (Result<T, N>, M)) -> Self {
match other {
(Ok(val), _) => Reportable::Value(val),
(Err(_), message) => Reportable::Failure(message.into()),
}
}
}
impl<T> From<CompilerResult<T>> for Reportable<T> {
fn from(other: CompilerResult<T>) -> Self {
Reportable::CompilerResult(other)
}
}
#[macro_export]
macro_rules! check {
($res:ident, $other:expr) => {{
use crate::result::Reportable;
if let Some(value) = $res.require(Reportable::from($other)) {
value
} else {
return $res;
}
}};
($res:ident, $other:expr, $message:expr) => {{
use crate::result::Reportable;
if let Some(value) = $res.require(Reportable::from(($other, $message))) {
value
} else {
return $res;
}
}};
}
macro_rules! assign {
($res:ident, $other:expr) => {{
use crate::result::Reportable;
if let Some(value) = $res.require(Reportable::from($other)) {
$res.set_value(value);
}
}};
($res:ident, $other:expr, $message:expr) => {{
use crate::result::Reportable;
if let Some(value) = $res.require(Reportable::from(($other, $message))) {
$res.set_value(value);
}
}};
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_status_passed() {
let mut res = CompilerResult::status_only("Test True");
res.assert(true, "asserting true");
assert_eq!(res.get_status(), Status::Unresolved);
}
#[test]
fn test_assert_false() {
let mut res = CompilerResult::status_only("Test False");
res.assert(false, "asserting false");
assert_eq!(res.get_status(), Status::Failed);
}
#[test]
fn test_require_passed() {
let mut res: CompilerResult<i32> = CompilerResult::new("Test i32");
res.set_value(8);
let mut res_2: CompilerResult<u32> = CompilerResult::new("Test u32");
res_2.set_value(10);
res.require(res_2);
assert_eq!(res.get_status(), Status::Passed);
}
#[test]
fn test_require_failed() {
let mut res: CompilerResult<i32> = CompilerResult::new("Test i32");
let mut res_2: CompilerResult<u32> = CompilerResult::new("Test u32");
res_2.assert(false, "asserting false");
res.require(res_2);
assert_eq!(res.get_status(), Status::Failed);
}
#[test]
fn test_collect_all_failed() {
let mut failed_result: CompilerResult<i32> = CompilerResult::unnamed();
let mut failed_result2: CompilerResult<i32> = CompilerResult::unnamed();
failed_result.fail();
failed_result2.fail();
let list_results = vec![failed_result, failed_result2];
let mut cr_result: CompilerResult<Vec<i32>> = CompilerResult::new("Task name");
cr_result.set_value(Vec::new());
cr_result.collect(list_results.into_iter());
assert_eq!(Status::Failed, cr_result.get_status());
}
#[test]
fn test_collect_one_failed() {
let mut failed_result: CompilerResult<i32> = CompilerResult::unnamed();
let mut passed_result: CompilerResult<i32> = CompilerResult::unnamed();
let mut passed_result2: CompilerResult<i32> = CompilerResult::unnamed();
failed_result.fail();
passed_result.set_value(0);
passed_result2.set_value(1);
let list_results = vec![failed_result, passed_result, passed_result2];
let mut cr_result: CompilerResult<Vec<i32>> = CompilerResult::new("Task name");
cr_result.set_value(Vec::new());
cr_result.collect(list_results.into_iter());
assert_eq!(Status::Failed, cr_result.get_status());
}
#[test]
fn test_collect_some_failed() {
let mut failed_result: CompilerResult<i32> = CompilerResult::unnamed();
let mut failed_result2: CompilerResult<i32> = CompilerResult::unnamed();
let mut passed_result: CompilerResult<i32> = CompilerResult::unnamed();
failed_result.fail();
failed_result2.fail();
passed_result.set_value(0);
let list_results = vec![failed_result, failed_result2, passed_result];
let mut cr_result: CompilerResult<Vec<i32>> = CompilerResult::new("Task name");
cr_result.set_value(Vec::new());
cr_result.collect(list_results.into_iter());
assert_eq!(Status::Failed, cr_result.get_status());
}
#[test]
fn test_collect_all_passed() {
let mut passed_result: CompilerResult<i32> = CompilerResult::unnamed();
let mut passed_result2: CompilerResult<i32> = CompilerResult::unnamed();
passed_result.set_value(1);
passed_result2.set_value(2);
let list_results = vec![passed_result, passed_result2];
let mut cr_result: CompilerResult<Vec<i32>> = CompilerResult::new("Task name");
cr_result.set_value(Vec::new());
cr_result.collect(list_results.into_iter());
assert_eq!(Status::Passed, cr_result.get_status());
}
}