mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-11-02 12:06:56 +08:00
feat(constraint_graph): make the constraint graph framework generic and move it into a separate crate (#3071)
This commit is contained in:
@ -4,11 +4,15 @@
|
||||
//! in the Euclid Rule DSL. These include standard control flow analyses like testing
|
||||
//! conflicting assertions, to Domain Specific Analyses making use of the
|
||||
//! [`Knowledge Graph Framework`](crate::dssa::graph).
|
||||
use hyperswitch_constraint_graph::{ConstraintGraph, Memoization};
|
||||
use rustc_hash::{FxHashMap, FxHashSet};
|
||||
|
||||
use super::{graph::Memoization, types::EuclidAnalysable};
|
||||
use crate::{
|
||||
dssa::{graph, state_machine, truth, types},
|
||||
dssa::{
|
||||
graph::CgraphExt,
|
||||
state_machine, truth,
|
||||
types::{self, EuclidAnalysable},
|
||||
},
|
||||
frontend::{
|
||||
ast,
|
||||
dir::{self, EuclidDirFilter},
|
||||
@ -203,12 +207,12 @@ fn perform_condition_analyses(
|
||||
|
||||
fn perform_context_analyses(
|
||||
context: &types::ConjunctiveContext<'_>,
|
||||
knowledge_graph: &graph::KnowledgeGraph<'_>,
|
||||
knowledge_graph: &ConstraintGraph<'_, dir::DirValue>,
|
||||
) -> Result<(), types::AnalysisError> {
|
||||
perform_condition_analyses(context)?;
|
||||
let mut memo = Memoization::new();
|
||||
knowledge_graph
|
||||
.perform_context_analysis(context, &mut memo)
|
||||
.perform_context_analysis(context, &mut memo, None)
|
||||
.map_err(|err| types::AnalysisError {
|
||||
error_type: types::AnalysisErrorType::GraphAnalysis(err, memo),
|
||||
metadata: Default::default(),
|
||||
@ -218,7 +222,7 @@ fn perform_context_analyses(
|
||||
|
||||
pub fn analyze<O: EuclidAnalysable + EuclidDirFilter>(
|
||||
program: ast::Program<O>,
|
||||
knowledge_graph: Option<&graph::KnowledgeGraph<'_>>,
|
||||
knowledge_graph: Option<&ConstraintGraph<'_, dir::DirValue>>,
|
||||
) -> Result<vir::ValuedProgram<O>, types::AnalysisError> {
|
||||
let dir_program = ast::lowering::lower_program(program)?;
|
||||
|
||||
@ -241,9 +245,14 @@ mod tests {
|
||||
use std::{ops::Deref, sync::Weak};
|
||||
|
||||
use euclid_macros::knowledge;
|
||||
use hyperswitch_constraint_graph as cgraph;
|
||||
|
||||
use super::*;
|
||||
use crate::{dirval, types::DummyOutput};
|
||||
use crate::{
|
||||
dirval,
|
||||
dssa::graph::{self, euclid_graph_prelude},
|
||||
types::DummyOutput,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn test_conflicting_assertion_detection() {
|
||||
@ -368,7 +377,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_negation_graph_analysis() {
|
||||
let graph = knowledge! {crate
|
||||
let graph = knowledge! {
|
||||
CaptureMethod(Automatic) ->> PaymentMethod(Card);
|
||||
};
|
||||
|
||||
@ -410,18 +419,18 @@ mod tests {
|
||||
.deref()
|
||||
.clone()
|
||||
{
|
||||
graph::AnalysisTrace::Value { predecessors, .. } => {
|
||||
let _value = graph::NodeValue::Value(dir::DirValue::PaymentMethod(
|
||||
cgraph::AnalysisTrace::Value { predecessors, .. } => {
|
||||
let _value = cgraph::NodeValue::Value(dir::DirValue::PaymentMethod(
|
||||
dir::enums::PaymentMethod::Card,
|
||||
));
|
||||
let _relation = graph::Relation::Positive;
|
||||
let _relation = cgraph::Relation::Positive;
|
||||
predecessors
|
||||
}
|
||||
_ => panic!("Expected Negation Trace for payment method = card"),
|
||||
};
|
||||
|
||||
let pred = match predecessor {
|
||||
Some(graph::ValueTracePredecessor::Mandatory(predecessor)) => predecessor,
|
||||
Some(cgraph::error::ValueTracePredecessor::Mandatory(predecessor)) => predecessor,
|
||||
_ => panic!("No predecessor found"),
|
||||
};
|
||||
assert_eq!(
|
||||
@ -433,11 +442,11 @@ mod tests {
|
||||
*Weak::upgrade(&pred)
|
||||
.expect("Expected Arc not found")
|
||||
.deref(),
|
||||
graph::AnalysisTrace::Value {
|
||||
value: graph::NodeValue::Value(dir::DirValue::CaptureMethod(
|
||||
cgraph::AnalysisTrace::Value {
|
||||
value: cgraph::NodeValue::Value(dir::DirValue::CaptureMethod(
|
||||
dir::enums::CaptureMethod::Automatic
|
||||
)),
|
||||
relation: graph::Relation::Positive,
|
||||
relation: cgraph::Relation::Positive,
|
||||
info: None,
|
||||
metadata: None,
|
||||
predecessors: None,
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,29 +1,30 @@
|
||||
use euclid_macros::knowledge;
|
||||
use once_cell::sync::Lazy;
|
||||
|
||||
use crate::dssa::graph;
|
||||
use crate::{dssa::graph::euclid_graph_prelude, frontend::dir};
|
||||
|
||||
pub static ANALYSIS_GRAPH: Lazy<graph::KnowledgeGraph<'_>> = Lazy::new(|| {
|
||||
knowledge! {crate
|
||||
// Payment Method should be `Card` for a CardType to be present
|
||||
PaymentMethod(Card) ->> CardType(any);
|
||||
pub static ANALYSIS_GRAPH: Lazy<hyperswitch_constraint_graph::ConstraintGraph<'_, dir::DirValue>> =
|
||||
Lazy::new(|| {
|
||||
knowledge! {
|
||||
// Payment Method should be `Card` for a CardType to be present
|
||||
PaymentMethod(Card) ->> CardType(any);
|
||||
|
||||
// Payment Method should be `PayLater` for a PayLaterType to be present
|
||||
PaymentMethod(PayLater) ->> PayLaterType(any);
|
||||
// Payment Method should be `PayLater` for a PayLaterType to be present
|
||||
PaymentMethod(PayLater) ->> PayLaterType(any);
|
||||
|
||||
// Payment Method should be `Wallet` for a WalletType to be present
|
||||
PaymentMethod(Wallet) ->> WalletType(any);
|
||||
// Payment Method should be `Wallet` for a WalletType to be present
|
||||
PaymentMethod(Wallet) ->> WalletType(any);
|
||||
|
||||
// Payment Method should be `BankRedirect` for a BankRedirectType to
|
||||
// be present
|
||||
PaymentMethod(BankRedirect) ->> BankRedirectType(any);
|
||||
// Payment Method should be `BankRedirect` for a BankRedirectType to
|
||||
// be present
|
||||
PaymentMethod(BankRedirect) ->> BankRedirectType(any);
|
||||
|
||||
// Payment Method should be `BankTransfer` for a BankTransferType to
|
||||
// be present
|
||||
PaymentMethod(BankTransfer) ->> BankTransferType(any);
|
||||
// Payment Method should be `BankTransfer` for a BankTransferType to
|
||||
// be present
|
||||
PaymentMethod(BankTransfer) ->> BankTransferType(any);
|
||||
|
||||
// Payment Method should be `GiftCard` for a GiftCardType to
|
||||
// be present
|
||||
PaymentMethod(GiftCard) ->> GiftCardType(any);
|
||||
}
|
||||
});
|
||||
// Payment Method should be `GiftCard` for a GiftCardType to
|
||||
// be present
|
||||
PaymentMethod(GiftCard) ->> GiftCardType(any);
|
||||
}
|
||||
});
|
||||
|
||||
@ -140,7 +140,10 @@ pub enum AnalysisErrorType {
|
||||
negation_metadata: Metadata,
|
||||
},
|
||||
#[error("Graph analysis error: {0:#?}")]
|
||||
GraphAnalysis(graph::AnalysisError, graph::Memoization),
|
||||
GraphAnalysis(
|
||||
graph::AnalysisError<dir::DirValue>,
|
||||
hyperswitch_constraint_graph::Memoization<dir::DirValue>,
|
||||
),
|
||||
#[error("State machine error")]
|
||||
StateMachine(dssa::state_machine::StateMachineError),
|
||||
#[error("Unsupported program key '{0}'")]
|
||||
|
||||
@ -4,4 +4,3 @@ pub mod dssa;
|
||||
pub mod enums;
|
||||
pub mod frontend;
|
||||
pub mod types;
|
||||
pub mod utils;
|
||||
|
||||
@ -1,3 +0,0 @@
|
||||
pub mod dense_map;
|
||||
|
||||
pub use dense_map::{DenseMap, EntityId};
|
||||
@ -1,224 +0,0 @@
|
||||
use std::{fmt, iter, marker::PhantomData, ops, slice, vec};
|
||||
|
||||
pub trait EntityId {
|
||||
fn get_id(&self) -> usize;
|
||||
fn with_id(id: usize) -> Self;
|
||||
}
|
||||
|
||||
pub struct DenseMap<K, V> {
|
||||
data: Vec<V>,
|
||||
_marker: PhantomData<K>,
|
||||
}
|
||||
|
||||
impl<K, V> DenseMap<K, V> {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
data: Vec::new(),
|
||||
_marker: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<K, V> Default for DenseMap<K, V> {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl<K, V> DenseMap<K, V>
|
||||
where
|
||||
K: EntityId,
|
||||
{
|
||||
pub fn push(&mut self, elem: V) -> K {
|
||||
let curr_len = self.data.len();
|
||||
self.data.push(elem);
|
||||
K::with_id(curr_len)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get(&self, idx: K) -> Option<&V> {
|
||||
self.data.get(idx.get_id())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_mut(&mut self, idx: K) -> Option<&mut V> {
|
||||
self.data.get_mut(idx.get_id())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn contains_key(&self, key: K) -> bool {
|
||||
key.get_id() < self.data.len()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn keys(&self) -> Keys<K> {
|
||||
Keys::new(0..self.data.len())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn into_keys(self) -> Keys<K> {
|
||||
Keys::new(0..self.data.len())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn values(&self) -> slice::Iter<'_, V> {
|
||||
self.data.iter()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn values_mut(&mut self) -> slice::IterMut<'_, V> {
|
||||
self.data.iter_mut()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn into_values(self) -> vec::IntoIter<V> {
|
||||
self.data.into_iter()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn iter(&self) -> Iter<'_, K, V> {
|
||||
Iter::new(self.data.iter())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn iter_mut(&mut self) -> IterMut<'_, K, V> {
|
||||
IterMut::new(self.data.iter_mut())
|
||||
}
|
||||
}
|
||||
|
||||
impl<K, V> fmt::Debug for DenseMap<K, V>
|
||||
where
|
||||
K: EntityId + fmt::Debug,
|
||||
V: fmt::Debug,
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_map().entries(self.iter()).finish()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Keys<K> {
|
||||
inner: ops::Range<usize>,
|
||||
_marker: PhantomData<K>,
|
||||
}
|
||||
|
||||
impl<K> Keys<K> {
|
||||
fn new(range: ops::Range<usize>) -> Self {
|
||||
Self {
|
||||
inner: range,
|
||||
_marker: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<K> Iterator for Keys<K>
|
||||
where
|
||||
K: EntityId,
|
||||
{
|
||||
type Item = K;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
self.inner.next().map(K::with_id)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Iter<'a, K, V> {
|
||||
inner: iter::Enumerate<slice::Iter<'a, V>>,
|
||||
_marker: PhantomData<K>,
|
||||
}
|
||||
|
||||
impl<'a, K, V> Iter<'a, K, V> {
|
||||
fn new(iter: slice::Iter<'a, V>) -> Self {
|
||||
Self {
|
||||
inner: iter.enumerate(),
|
||||
_marker: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, K, V> Iterator for Iter<'a, K, V>
|
||||
where
|
||||
K: EntityId,
|
||||
{
|
||||
type Item = (K, &'a V);
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
self.inner.next().map(|(id, val)| (K::with_id(id), val))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct IterMut<'a, K, V> {
|
||||
inner: iter::Enumerate<slice::IterMut<'a, V>>,
|
||||
_marker: PhantomData<K>,
|
||||
}
|
||||
|
||||
impl<'a, K, V> IterMut<'a, K, V> {
|
||||
fn new(iter: slice::IterMut<'a, V>) -> Self {
|
||||
Self {
|
||||
inner: iter.enumerate(),
|
||||
_marker: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, K, V> Iterator for IterMut<'a, K, V>
|
||||
where
|
||||
K: EntityId,
|
||||
{
|
||||
type Item = (K, &'a mut V);
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
self.inner.next().map(|(id, val)| (K::with_id(id), val))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct IntoIter<K, V> {
|
||||
inner: iter::Enumerate<vec::IntoIter<V>>,
|
||||
_marker: PhantomData<K>,
|
||||
}
|
||||
|
||||
impl<K, V> IntoIter<K, V> {
|
||||
fn new(iter: vec::IntoIter<V>) -> Self {
|
||||
Self {
|
||||
inner: iter.enumerate(),
|
||||
_marker: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<K, V> Iterator for IntoIter<K, V>
|
||||
where
|
||||
K: EntityId,
|
||||
{
|
||||
type Item = (K, V);
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
self.inner.next().map(|(id, val)| (K::with_id(id), val))
|
||||
}
|
||||
}
|
||||
|
||||
impl<K, V> IntoIterator for DenseMap<K, V>
|
||||
where
|
||||
K: EntityId,
|
||||
{
|
||||
type Item = (K, V);
|
||||
type IntoIter = IntoIter<K, V>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
IntoIter::new(self.data.into_iter())
|
||||
}
|
||||
}
|
||||
|
||||
impl<K, V> FromIterator<V> for DenseMap<K, V>
|
||||
where
|
||||
K: EntityId,
|
||||
{
|
||||
fn from_iter<T>(iter: T) -> Self
|
||||
where
|
||||
T: IntoIterator<Item = V>,
|
||||
{
|
||||
Self {
|
||||
data: Vec::from_iter(iter),
|
||||
_marker: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user