print page poggers2
This commit is contained in:
2
client
2
client
Submodule client updated: 2465eb9ca9...f85bc82e85
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
pub mod search;
|
pub mod search;
|
||||||
pub mod grpc_error;
|
pub mod grpc_error;
|
||||||
|
pub mod relationship;
|
||||||
|
|
||||||
pub mod proto {
|
pub mod proto {
|
||||||
pub mod komp_ac {
|
pub mod komp_ac {
|
||||||
|
|||||||
97
common/src/relationship.rs
Normal file
97
common/src/relationship.rs
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::collections::{HashMap, HashSet, VecDeque};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||||
|
pub struct RelationshipHop {
|
||||||
|
pub from_table: String,
|
||||||
|
pub to_table: String,
|
||||||
|
pub fk_source_table: String,
|
||||||
|
pub fk_column: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
pub struct RelationshipLink {
|
||||||
|
pub source_table: String,
|
||||||
|
pub target_table: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn resolve_relationship_path(
|
||||||
|
start: &str,
|
||||||
|
end: &str,
|
||||||
|
links: &[RelationshipLink],
|
||||||
|
) -> Result<Vec<RelationshipHop>, String> {
|
||||||
|
if start == end {
|
||||||
|
return Ok(Vec::new());
|
||||||
|
}
|
||||||
|
let mut queue = VecDeque::from([(start.to_string(), Vec::new())]);
|
||||||
|
let mut best_depth: HashMap<String, usize> = HashMap::from([(start.to_string(), 0)]);
|
||||||
|
let mut matches = Vec::new();
|
||||||
|
let mut match_depth = None;
|
||||||
|
|
||||||
|
while let Some((table, path)) = queue.pop_front() {
|
||||||
|
if match_depth.is_some_and(|depth| path.len() >= depth) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for link in links {
|
||||||
|
let next = if link.source_table == table {
|
||||||
|
Some(link.target_table.as_str())
|
||||||
|
} else if link.target_table == table {
|
||||||
|
Some(link.source_table.as_str())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
let Some(next) = next else { continue };
|
||||||
|
if path
|
||||||
|
.iter()
|
||||||
|
.any(|hop: &RelationshipHop| hop.from_table == next)
|
||||||
|
|| next == start
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let mut next_path = path.clone();
|
||||||
|
next_path.push(RelationshipHop {
|
||||||
|
from_table: table.clone(),
|
||||||
|
to_table: next.to_string(),
|
||||||
|
fk_source_table: link.source_table.clone(),
|
||||||
|
fk_column: format!("{}_id", link.target_table),
|
||||||
|
});
|
||||||
|
if next == end {
|
||||||
|
match_depth = Some(next_path.len());
|
||||||
|
matches.push(next_path);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let depth = next_path.len();
|
||||||
|
if best_depth.get(next).is_none_or(|known| depth <= *known) {
|
||||||
|
best_depth.insert(next.to_string(), depth);
|
||||||
|
queue.push_back((next.to_string(), next_path));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
match matches.as_slice() {
|
||||||
|
[path] => Ok(path.clone()),
|
||||||
|
[] => Err(format!("No FK path from '{}' to '{}'", start, end)),
|
||||||
|
_ => Err(format!(
|
||||||
|
"Multiple equally short FK paths from '{}' to '{}'",
|
||||||
|
start, end
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn resolve_anchored_relationship_path(
|
||||||
|
source: &str,
|
||||||
|
target: &str,
|
||||||
|
anchor: &str,
|
||||||
|
links: &[RelationshipLink],
|
||||||
|
) -> Result<Vec<RelationshipHop>, String> {
|
||||||
|
let mut path = resolve_relationship_path(source, anchor, links)?;
|
||||||
|
path.extend(resolve_relationship_path(anchor, target, links)?);
|
||||||
|
let mut tables = HashSet::from([source.to_string()]);
|
||||||
|
if path.iter().any(|hop| !tables.insert(hop.to_table.clone())) {
|
||||||
|
return Err(format!(
|
||||||
|
"The FK route from '{}' to '{}' via '{}' revisits a table",
|
||||||
|
source, target, anchor
|
||||||
|
));
|
||||||
|
}
|
||||||
|
Ok(path)
|
||||||
|
}
|
||||||
2
server
2
server
Submodule server updated: af3e1cd2b3...56c988c2e3
Reference in New Issue
Block a user