use panproto_schema::{Constraint, Edge, Schema, Vertex};
#[must_use]
pub fn find_roots<'a>(schema: &'a Schema, structural_edge_kinds: &[&str]) -> Vec<&'a Vertex> {
let mut roots: Vec<&Vertex> = schema
.vertices
.values()
.filter(|v| {
let incoming = schema.incoming_edges(&v.id);
!incoming
.iter()
.any(|e| structural_edge_kinds.contains(&&*e.kind))
})
.collect();
roots.sort_by(|a, b| a.id.cmp(&b.id));
roots
}
#[must_use]
pub fn children_by_edge<'a>(
schema: &'a Schema,
parent: &str,
edge_kind: &str,
) -> Vec<(&'a Edge, &'a Vertex)> {
let mut children: Vec<(&Edge, &Vertex)> = schema
.outgoing_edges(parent)
.iter()
.filter(|e| e.kind == edge_kind)
.filter_map(|e| schema.vertices.get(&e.tgt).map(|v| (e, v)))
.collect();
children.sort_by(|a, b| {
let a_name = a.0.name.as_deref().unwrap_or("");
let b_name = b.0.name.as_deref().unwrap_or("");
a_name.cmp(b_name)
});
children
}
#[must_use]
pub fn constraint_value<'a>(schema: &'a Schema, vertex_id: &str, sort: &str) -> Option<&'a str> {
schema
.constraints
.get(vertex_id)?
.iter()
.find(|c| c.sort == sort)
.map(|c| c.value.as_str())
}
#[must_use]
pub fn vertex_constraints<'a>(schema: &'a Schema, vertex_id: &str) -> Vec<&'a Constraint> {
schema
.constraints
.get(vertex_id)
.map(|cs| cs.iter().collect())
.unwrap_or_default()
}
pub struct IndentWriter {
buf: String,
level: usize,
indent_str: &'static str,
}
impl IndentWriter {
#[must_use]
pub const fn new(indent_str: &'static str) -> Self {
Self {
buf: String::new(),
level: 0,
indent_str,
}
}
pub const fn indent(&mut self) {
self.level += 1;
}
pub const fn dedent(&mut self) {
self.level = self.level.saturating_sub(1);
}
pub fn line(&mut self, s: &str) {
for _ in 0..self.level {
self.buf.push_str(self.indent_str);
}
self.buf.push_str(s);
self.buf.push('\n');
}
pub fn blank(&mut self) {
self.buf.push('\n');
}
pub fn raw(&mut self, s: &str) {
self.buf.push_str(s);
}
#[must_use]
pub fn finish(self) -> String {
self.buf
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn indent_writer_basic() {
let mut w = IndentWriter::new(" ");
w.line("message Foo {");
w.indent();
w.line("string bar = 1;");
w.dedent();
w.line("}");
let result = w.finish();
assert_eq!(result, "message Foo {\n string bar = 1;\n}\n");
}
}