1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175
//! Prolog results.
//!
//! Functions in swipl-rs that interact with SWI-Prolog generally
//! return a [PrologResult]. This allows you to use them with the `?`
//! syntax in situations where you need to call multiple such
//! functions, and each failure or exception is a reason to exit
//! early.
//!
//! This module also provides some transformations on prolog results.
use thiserror::Error;
use crate::context::{Context, QueryableContextType};
/// A prolog error.
///
/// This is either a failure or an exception. In case of an exception,
/// whowever returned the exception was also supposed to raise an
/// exception on the context.
#[derive(Error, Debug, PartialEq, Eq)]
pub enum PrologError {
#[error("prolog function failed")]
Failure,
#[error("prolog function threw an exception")]
Exception,
}
impl PrologError {
/// Returns true if this error is a failure.
pub fn is_failure(&self) -> bool {
matches!(self, PrologError::Failure)
}
/// Returns true if this error is an exception.
pub fn is_exception(&self) -> bool {
matches!(self, PrologError::Exception)
}
}
/// Unit type for errors which can only be an exception.
#[derive(Debug)]
pub struct PrologException;
impl From<PrologException> for PrologError {
fn from(_val: PrologException) -> PrologError {
PrologError::Exception
}
}
/// The main result type that most interface functions return.
pub type PrologResult<R> = Result<R, PrologError>;
/// Result type for operations that cannot fail, but can throw an exception.
pub type NonFailingPrologResult<R> = Result<R, PrologException>;
/// Result type for expressing failure as a boolean instead of an Err.
pub type BoolPrologResult = Result<bool, PrologException>;
/// result type for expressing failure as an Option type instead of an Err.
pub type OptPrologResult<R> = Result<Option<R>, PrologException>;
/// Transforms a `PrologResult<()>` into a [BoolPrologResult], allowing more easy use from an if block.
///
/// Example:
/// ```
///# use swipl::prelude::*;
///# fn main() -> PrologResult<()> {
///# let engine = Engine::new();
///# let activation = engine.activate();
///# let context: Context<_> = activation.into();
///#
///# let term = context.new_term_ref();
/// if attempt(term.unify(42_u64))? {
/// // the unification succeeded
/// } else {
/// // the unification failed
/// }
///# Ok(())
///# }
/// ```
pub fn attempt(r: PrologResult<()>) -> BoolPrologResult {
match r {
Ok(()) => Ok(true),
Err(PrologError::Failure) => Ok(false),
Err(PrologError::Exception) => Err(PrologException),
}
}
/// Transforms a [PrologResult] into an [OptPrologResult], allowing more easy use from an if block.
///
/// Example:
/// ```
///# use swipl::prelude::*;
///# fn main() -> PrologResult<()> {
///# let engine = Engine::new();
///# let activation = engine.activate();
///# let context: Context<_> = activation.into();
///#
///# let term = context.new_term_ref();
/// if let Some(num) = attempt_opt(term.get::<u64>())? {
/// // term contained an u64
/// } else {
/// // term did not contain an u64
/// }
///# Ok(())
///# }
/// ```
pub fn attempt_opt<R>(r: PrologResult<R>) -> OptPrologResult<R> {
match r {
Ok(r) => Ok(Some(r)),
Err(PrologError::Failure) => Ok(None),
Err(PrologError::Exception) => Err(PrologException),
}
}
/// Turn a boolean into a prolog result.
///
/// True will become `Ok(())`, and false will become
/// `Err(PrologError::Failure)`.
pub fn into_prolog_result(b: bool) -> PrologResult<()> {
match b {
true => Ok(()),
false => Err(PrologError::Failure),
}
}
/// Return a failure.
///
/// This is a shorthand for `Err(PrologError::Failure)`.
pub fn fail() -> PrologResult<()> {
Err(PrologError::Failure)
}
pub enum PrologStringError {
Failure,
Exception(String),
}
pub type PrologStringResult<T> = Result<T, PrologStringError>;
pub fn result_to_string_result<C: QueryableContextType, T>(
c: &Context<C>,
r: PrologResult<T>,
) -> PrologStringResult<T> {
match r {
Ok(r) => Ok(r),
Err(PrologError::Failure) => Err(PrologStringError::Failure),
Err(PrologError::Exception) => {
let r = c.with_exception(|e| {
let e = e.expect("prolog exception but no exception in prolog engine");
c.string_from_term(e)
});
c.clear_exception();
match r {
Ok(s) => Err(PrologStringError::Exception(format!(
"prolog had the following exception: {}",
s
))),
Err(PrologError::Failure) => Err(PrologStringError::Exception(
"prolog failed while retrieving string from previous error".to_string(),
)),
Err(PrologError::Exception) => Err(PrologStringError::Exception(
"prolog threw exception while retrieving string from previous error"
.to_string(),
)),
}
}
}
}
pub fn unwrap_result<C: QueryableContextType, T>(c: &Context<C>, r: PrologResult<T>) -> T {
match result_to_string_result(c, r) {
Ok(r) => r,
Err(PrologStringError::Failure) => panic!("prolog failed"),
Err(PrologStringError::Exception(s)) => panic!("{}", s),
}
}