use crate::io::ModelWriter;
use crate::model::shapes::{HasTraits, MemberShape, ShapeKind, TopLevelShape};
use crate::model::values::Value;
use crate::model::{HasIdentity, Model, ShapeID};
use std::collections::HashMap;
use std::io::Write;
#[derive(Debug)]
pub struct LineOrientedWriter {}
pub fn make_line_oriented_form(model: &Model) -> Vec<String> {
let mut strings = Default::default();
for (key, value) in model.metadata() {
value_into_strings(
&format!("{}{}{}{}", META_PREFIX, SEGMENT_SEP, key, VALUE_SEP),
value,
&mut strings,
);
}
for shape in model.shapes() {
shape_into_strings(shape, &mut strings)
}
strings.sort();
strings
}
impl Default for LineOrientedWriter {
fn default() -> Self {
Self {}
}
}
impl ModelWriter for LineOrientedWriter {
fn write(&mut self, w: &mut impl Write, model: &Model) -> crate::error::Result<()> {
for line in make_line_oriented_form(model) {
writeln!(w, "{}", line)?;
}
Ok(())
}
}
const META_PREFIX: &str = "meta";
const TRAIT_PREFIX: &str = "trait";
const SEGMENT_SEP: &str = "::";
const TARGET_SEP: &str = "=>";
const VALUE_SEP: &str = "<=";
fn line_prefix(shape_type: &str, shape: &TopLevelShape) -> String {
format!("{}{}{}", shape_type, SEGMENT_SEP, shape.id())
}
fn shape_into_strings(shape: &TopLevelShape, strings: &mut Vec<String>) {
let prefix = match shape.body() {
ShapeKind::Simple(v) => {
let prefix = line_prefix(&v.to_string(), shape);
strings.push(prefix.clone());
prefix
}
ShapeKind::List(v) => {
let prefix = line_prefix("list", shape);
strings.push(prefix.clone());
member_into_strings(&prefix, v.member(), strings);
prefix
}
ShapeKind::Set(v) => {
let prefix = line_prefix("set", shape);
strings.push(prefix.clone());
member_into_strings(&prefix, v.member(), strings);
prefix
}
ShapeKind::Map(v) => {
let prefix = line_prefix("map", shape);
strings.push(prefix.clone());
member_into_strings(&prefix, v.key(), strings);
member_into_strings(&prefix, v.value(), strings);
prefix
}
ShapeKind::Structure(v) => {
let prefix = line_prefix("structure", shape);
strings.push(prefix.clone());
for member in v.members() {
member_into_strings(&prefix, member, strings);
}
prefix
}
ShapeKind::Union(v) => {
let prefix = line_prefix("union", shape);
strings.push(prefix.clone());
for member in v.members() {
member_into_strings(&prefix, member, strings);
}
prefix
}
ShapeKind::Service(v) => {
let prefix = line_prefix("service", shape);
strings.push(prefix.clone());
strings.push(format!(
"{}{}version{}{:?}",
prefix,
SEGMENT_SEP,
VALUE_SEP,
v.version()
));
for id in v.operations() {
strings.push(format!("{}{}", prefix, member_target("operation", id)));
}
for id in v.resources() {
strings.push(format!("{}{}", prefix, member_target("resource", id)));
}
for (id, ln) in v.renames() {
strings.push(format!(
"{}{}rename{}{}{}{}",
prefix, SEGMENT_SEP, SEGMENT_SEP, id, VALUE_SEP, ln
));
}
prefix
}
ShapeKind::Operation(v) => {
let prefix = line_prefix("operation", shape);
strings.push(prefix.clone());
if let Some(id) = v.input() {
strings.push(format!("{}{}", prefix, member_target("input", id)));
}
if let Some(id) = v.output() {
strings.push(format!("{}{}", prefix, member_target("output", id)));
}
for id in v.errors() {
strings.push(format!("{}{}", prefix, member_target("error", id)));
}
prefix
}
ShapeKind::Resource(v) => {
let prefix = line_prefix("resource", shape);
strings.push(prefix.clone());
let ident_prefix = format!("{}identifier", SEGMENT_SEP);
for (k, id) in v.identifiers() {
strings.push(format!(
"{}{}{}",
prefix,
ident_prefix,
member_target(&k.to_string(), id)
));
}
if let Some(id) = v.create() {
strings.push(format!("{}{}", prefix, member_target("create", id)));
}
if let Some(id) = v.put() {
strings.push(format!("{}{}", prefix, member_target("put", id)));
}
for id in v.read() {
strings.push(format!("{}{}", prefix, member_target("read", id)));
}
if let Some(id) = v.update() {
strings.push(format!("{}{}", prefix, member_target("update", id)));
}
if let Some(id) = v.delete() {
strings.push(format!("{}{}", prefix, member_target("delete", id)));
}
for id in v.list() {
strings.push(format!("{}{}", prefix, member_target("list", id)));
}
for id in v.operations() {
strings.push(format!("{}{}", prefix, member_target("operation", id)));
}
for id in v.collection_operations() {
strings.push(format!(
"{}{}",
prefix,
member_target("collection_operation", id)
));
}
for id in v.resources() {
strings.push(format!("{}{}", prefix, member_target("resource", id)));
}
prefix
}
ShapeKind::Unresolved => {
let prefix = line_prefix("unresolved", shape);
strings.push(prefix.clone());
prefix
}
};
let prefix = format!("{}{}{}{}", prefix, SEGMENT_SEP, TRAIT_PREFIX, SEGMENT_SEP);
traits_into_strings(&prefix, shape.traits(), strings);
}
fn member_target(member_name: &str, id: &ShapeID) -> String {
format!("{}{}{}{}", SEGMENT_SEP, member_name, TARGET_SEP, id)
}
fn value_into_strings(prefix: &str, value: &Value, strings: &mut Vec<String>) {
match value {
Value::Array(vs) => {
if vs.is_empty() {
strings.push(format!("{}[]", prefix));
} else {
for (i, v) in vs.iter().enumerate() {
let prefix = format!("{}[{}]=", prefix, i);
value_into_strings(&prefix, v, strings);
}
}
}
Value::Object(vo) => {
if vo.is_empty() {
strings.push(format!("{}{{}}", prefix));
} else {
for (k, v) in vo {
let prefix = format!("{}{{{}}}=", prefix, k);
value_into_strings(&prefix, v, strings);
}
}
}
Value::Number(v) => strings.push(format!("{}{}", prefix, v)),
Value::Boolean(v) => strings.push(format!("{}{}", prefix, v)),
Value::String(v) => strings.push(format!("{}\"{}\"", prefix, v)),
Value::None => strings.push(format!("{}()", prefix)),
}
}
fn member_into_strings(prefix: &str, member: &MemberShape, strings: &mut Vec<String>) {
let prefix = format!("{}{}{}", prefix, SEGMENT_SEP, member.id());
strings.push(format!("{}{}{}", prefix, TARGET_SEP, member.target()));
let prefix = format!("{}{}{}{}", prefix, SEGMENT_SEP, TRAIT_PREFIX, SEGMENT_SEP);
traits_into_strings(&prefix, member.traits(), strings);
}
fn traits_into_strings(
prefix: &str,
traits: &HashMap<ShapeID, Option<Value>>,
strings: &mut Vec<String>,
) {
for (shape_id, value) in traits {
match value {
None => {
strings.push(format!("{}{}", prefix, shape_id));
}
Some(value) => {
value_into_strings(
&format!("{}{}{}", prefix, shape_id, VALUE_SEP),
value,
strings,
);
}
}
}
}