use atelier_core::error::{Error as ModelError, Result as ModelResult};
use atelier_core::io::ModelWriter;
use atelier_core::model::shapes::{
AppliedTraits, ListOrSet, Map, Operation, Resource, Service, Simple, StructureOrUnion,
};
use atelier_core::model::visitor::{walk_model, ModelVisitor};
use atelier_core::model::{HasIdentity, Model, ShapeID};
use atelier_core::syntax::{
MEMBER_COLLECTION_OPERATIONS, MEMBER_CREATE, MEMBER_DELETE, MEMBER_ERRORS, MEMBER_INPUT,
MEMBER_KEY, MEMBER_LIST, MEMBER_MEMBER, MEMBER_OPERATIONS, MEMBER_OUTPUT, MEMBER_PUT,
MEMBER_READ, MEMBER_RESOURCES, MEMBER_UPDATE, MEMBER_VALUE, SHAPE_LIST, SHAPE_MAP,
SHAPE_OPERATION, SHAPE_RESOURCE, SHAPE_SERVICE, SHAPE_SET, SHAPE_STRUCTURE, SHAPE_UNION,
};
use std::cell::{RefCell, RefMut};
use std::io::Write;
#[allow(clippy::upper_case_acronyms)]
#[derive(Debug)]
pub struct GraphMLWriter {}
struct VisitorState<'a, W: Write> {
edge_count: u16,
writer: &'a mut W,
}
#[allow(clippy::upper_case_acronyms)]
struct GraphMLVisitor<'a, W: Write> {
state: RefCell<VisitorState<'a, W>>,
}
impl Default for GraphMLWriter {
fn default() -> Self {
Self {}
}
}
impl ModelWriter for GraphMLWriter {
fn write(&mut self, w: &mut impl Write, model: &Model) -> ModelResult<()> {
writeln!(w, r#"<?xml version="1.0" encoding="UTF-8"?>"#)?;
writeln!(
w,
r#"<graphml xmlns="http://graphml.graphdrawing.org/xmlns""#
)?;
writeln!(
w,
r#" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance""#
)?;
writeln!(
w,
r#" xsi:schemaLocation="http://graphml.graphdrawing.org/xmlns"#
)?;
writeln!(
w,
r#" http://graphml.graphdrawing.org/xmlns/1.0/graphml.xsd">"#
)?;
self.write_keys(w)?;
writeln!(w, r#" <graph id="model" edgedefault="directed">"#)?;
writeln!(
w,
r#" <data key="smithy.version">{}</data>"#,
model.smithy_version()
)?;
let visitor = GraphMLVisitor {
state: RefCell::new(VisitorState {
edge_count: 0,
writer: w,
}),
};
walk_model(model, &visitor)?;
writeln!(w, r#" </graph>"#)?;
writeln!(w, r#"</graphml>"#)?;
Ok(())
}
}
impl GraphMLWriter {
fn write_keys(&mut self, w: &mut impl Write) -> ModelResult<()> {
writeln!(
w,
r#" <key id="smithy-version" for="graph" attr.name="smithy.version" attr.type="string"/>"#
)?;
writeln!(
w,
r#" <key id="type" for="node" attr.name="type" attr.type="string"/>"#
)?;
writeln!(
w,
r#" <key id="version" for="node" attr.name="version" attr.type="string"/>"#
)?;
writeln!(
w,
r#" <key id="trait" for="edge" attr.name="trait" attr.type="boolean"/>"#
)?;
writeln!(
w,
r#" <key id="member" for="edge" attr.name="member" attr.type="string">"#
)?;
writeln!(w, r#" <default>member</default>"#)?;
writeln!(w, r#" </key>"#)?;
Ok(())
}
}
impl<'a, W: Write> ModelVisitor for GraphMLVisitor<'a, W> {
type Error = ModelError;
fn simple_shape(
&self,
id: &ShapeID,
traits: &AppliedTraits,
value: &Simple,
) -> Result<(), Self::Error> {
self.node(id, &value.to_string())?;
self.traits(id, traits)?;
Ok(())
}
fn list(
&self,
id: &ShapeID,
traits: &AppliedTraits,
value: &ListOrSet,
) -> Result<(), Self::Error> {
self.node(id, SHAPE_LIST)?;
self.traits(id, traits)?;
self.member(id, value.member().target(), MEMBER_MEMBER)?;
Ok(())
}
fn set(
&self,
id: &ShapeID,
traits: &AppliedTraits,
value: &ListOrSet,
) -> Result<(), Self::Error> {
self.node(id, SHAPE_SET)?;
self.traits(id, traits)?;
self.member(id, value.member().target(), MEMBER_MEMBER)?;
Ok(())
}
fn map(&self, id: &ShapeID, traits: &AppliedTraits, value: &Map) -> Result<(), Self::Error> {
self.node(id, SHAPE_MAP)?;
self.traits(id, traits)?;
self.member(id, value.key().target(), MEMBER_KEY)?;
self.member(id, value.value().target(), MEMBER_VALUE)?;
Ok(())
}
fn structure(
&self,
id: &ShapeID,
traits: &AppliedTraits,
value: &StructureOrUnion,
) -> Result<(), Self::Error> {
self.node(id, SHAPE_STRUCTURE)?;
self.traits(id, traits)?;
for member in value.members() {
self.member(id, member.target(), &member.id().to_string())?;
}
Ok(())
}
fn union(
&self,
id: &ShapeID,
traits: &AppliedTraits,
value: &StructureOrUnion,
) -> Result<(), Self::Error> {
self.node(id, SHAPE_UNION)?;
self.traits(id, traits)?;
for member in value.members() {
self.member(id, member.target(), &member.id().to_string())?;
}
Ok(())
}
fn operation(
&self,
id: &ShapeID,
traits: &AppliedTraits,
value: &Operation,
) -> Result<(), Self::Error> {
self.node(id, SHAPE_OPERATION)?;
self.traits(id, traits)?;
if let Some(target) = value.input() {
self.member(id, target, MEMBER_INPUT)?;
}
if let Some(target) = value.output() {
self.member(id, target, MEMBER_OUTPUT)?;
}
for target in value.errors() {
self.member(id, target, MEMBER_ERRORS)?;
}
Ok(())
}
fn service(
&self,
id: &ShapeID,
traits: &AppliedTraits,
value: &Service,
) -> Result<(), Self::Error> {
{
let mut state = self.state.borrow_mut();
writeln!(state.writer, r#" <node id="{}">"#, id)?;
writeln!(
state.writer,
r#" <data key="type">{}</data>"#,
SHAPE_SERVICE
)?;
writeln!(
state.writer,
r#" <data key="version">{}</data>"#,
value.version()
)?;
writeln!(state.writer, r#" </node>"#)?;
}
self.traits(id, traits)?;
for target in value.operations() {
self.member(id, target, MEMBER_OPERATIONS)?;
}
for target in value.resources() {
self.member(id, target, MEMBER_RESOURCES)?;
}
Ok(())
}
fn resource(
&self,
id: &ShapeID,
traits: &AppliedTraits,
value: &Resource,
) -> Result<(), Self::Error> {
self.node(id, SHAPE_RESOURCE)?;
self.traits(id, traits)?;
if let Some(target) = value.create() {
self.member(id, target, MEMBER_CREATE)?;
}
if let Some(target) = value.put() {
self.member(id, target, MEMBER_PUT)?;
}
if let Some(target) = value.read() {
self.member(id, target, MEMBER_READ)?;
}
if let Some(target) = value.update() {
self.member(id, target, MEMBER_UPDATE)?;
}
if let Some(target) = value.delete() {
self.member(id, target, MEMBER_DELETE)?;
}
if let Some(target) = value.list() {
self.member(id, target, MEMBER_LIST)?;
}
for target in value.operations() {
self.member(id, target, MEMBER_OPERATIONS)?;
}
for target in value.collection_operations() {
self.member(id, target, MEMBER_COLLECTION_OPERATIONS)?;
}
for target in value.resources() {
self.member(id, target, MEMBER_RESOURCES)?;
}
Ok(())
}
}
impl<'a, W: Write> GraphMLVisitor<'a, W> {
fn node(&self, id: &ShapeID, type_str: &str) -> ModelResult<()> {
let mut state = self.state.borrow_mut();
writeln!(state.writer, r#" <node id="{}">"#, id)?;
writeln!(
state.writer,
r#" <data key="type">{}</data>"#,
type_str
)?;
writeln!(state.writer, r#" </node>"#)?;
Ok(())
}
fn member(&self, source: &ShapeID, target: &ShapeID, name: &str) -> ModelResult<()> {
let mut state = self.state.borrow_mut();
let edge_id = Self::edge_id(&mut state);
writeln!(
state.writer,
r#" <edge id="{}" source="{}" target="{}">"#,
edge_id, source, target
)?;
writeln!(
state.writer,
r#" <data key="member">{}</data>"#,
name
)?;
writeln!(state.writer, r#" </edge>"#)?;
Ok(())
}
fn traits(&self, id: &ShapeID, traits: &AppliedTraits) -> ModelResult<()> {
let mut state = self.state.borrow_mut();
for trait_id in traits.keys() {
let edge_id = Self::edge_id(&mut state);
writeln!(
state.writer,
r#" <edge id="{}" source="{}" target="{}">"#,
edge_id, id, trait_id
)?;
writeln!(state.writer, r#" <data key="trait">true</data>"#)?;
writeln!(state.writer, r#" </edge>"#)?;
}
Ok(())
}
fn edge_id(state: &mut RefMut<'_, VisitorState<'a, W>>) -> String {
let id = state.edge_count;
state.edge_count = id + 1;
format!("e{}", id)
}
}