///|
/// Text ingest for local Protocol Contract fixtures.
///|
pub(all) suberror ProtocolIngestError {
ParseFailed(String)
MissingField(String)
InvalidField(String, String)
} derive(Debug)
///|
pub fn parse_global_protocol_text(
text : String,
) -> GlobalProtocol raise ProtocolIngestError {
let trimmed = text[:].trim()
if trimmed.has_prefix("{") {
parse_global_protocol_json(text)
} else {
parse_global_protocol_yaml(text)
}
}
///|
pub fn parse_global_protocol_json(
text : String,
) -> GlobalProtocol raise ProtocolIngestError {
let parsed = @json.parse(text[:]) catch {
err => raise ProtocolIngestError::ParseFailed(err.to_string())
}
let protocol_id = json_required_string(parsed, "protocol_id")
let mut protocol = GlobalProtocol::new(protocol_id~)
match parsed {
{ "messages": Array(messages), .. } =>
for message_json in messages {
protocol = append_message_json(protocol, message_json)
}
_ => raise ProtocolIngestError::MissingField("messages")
}
match parsed {
{ "terminal_labels": Array(labels), .. } =>
for label_json in labels {
protocol = protocol.with_terminal_label(
label=decode_json_string(label_json, "terminal_labels"),
)
}
_ => ()
}
protocol
}
///|
pub fn parse_global_protocol_yaml(
text : String,
) -> GlobalProtocol raise ProtocolIngestError {
let fields = parse_key_value_lines(text)
let protocol_id = required_field(fields, "protocol_id")
let mut protocol = GlobalProtocol::new(protocol_id~)
for field in fields {
let (key, value) = field
if key == "message" {
protocol = append_message_row(protocol, value)
} else if key == "terminal_label" {
protocol = protocol.with_terminal_label(label=value)
}
}
protocol
}
///|
fn append_message_json(
protocol : GlobalProtocol,
json : Json,
) -> GlobalProtocol raise ProtocolIngestError {
protocol.send(
from=parse_role_name(json_required_string(json, "from"), "from"),
to=parse_role_name(json_required_string(json, "to"), "to"),
label=json_required_string(json, "label"),
object_id=json_required_string(json, "object_id"),
)
}
///|
fn append_message_row(
protocol : GlobalProtocol,
value : String,
) -> GlobalProtocol raise ProtocolIngestError {
let parts = value.split(",").to_array()
guard parts.length() == 4 else {
raise ProtocolIngestError::InvalidField("message", value)
}
protocol.send(
from=parse_role_name(clean_value(parts[0].trim().to_owned()), "from"),
to=parse_role_name(clean_value(parts[1].trim().to_owned()), "to"),
label=clean_value(parts[2].trim().to_owned()),
object_id=clean_value(parts[3].trim().to_owned()),
)
}
///|
fn parse_role_name(
value : String,
field : String,
) -> Role raise ProtocolIngestError {
let clean = clean_value(value)
if clean == "" {
raise ProtocolIngestError::InvalidField(field, clean)
}
role(name=clean)
}
///|
fn parse_key_value_lines(
text : String,
) -> Array[(String, String)] raise ProtocolIngestError {
let fields : Array[(String, String)] = []
for raw_line in text.split("\n") {
let line = raw_line.trim().to_owned()
if line == "" || line.has_prefix("#") || line == "---" {
()
} else {
fields.push(parse_key_value_line(line))
}
}
fields
}
///|
fn parse_key_value_line(
line : String,
) -> (String, String) raise ProtocolIngestError {
let parts = if line.contains(":") {
line.split(":").to_array()
} else {
line.split("=").to_array()
}
guard parts.length() == 2 else {
raise ProtocolIngestError::ParseFailed("invalid line: " + line)
}
(
normalized(parts[0].trim().to_owned()),
clean_value(parts[1].trim().to_owned()),
)
}
///|
fn required_field(
fields : Array[(String, String)],
name : String,
) -> String raise ProtocolIngestError {
match optional_field(fields, name) {
Some(value) => value
None => raise ProtocolIngestError::MissingField(name)
}
}
///|
fn optional_field(fields : Array[(String, String)], name : String) -> String? {
let mut found : String? = None
for field in fields {
let (key, value) = field
if key == name {
found = Some(value)
}
}
found
}
///|
fn json_required_string(
json : Json,
field : String,
) -> String raise ProtocolIngestError {
match json_optional_string(json, field) {
Some(value) => value
None => raise ProtocolIngestError::MissingField(field)
}
}
///|
fn json_optional_string(
json : Json,
field : String,
) -> String? raise ProtocolIngestError {
match (field, json) {
("protocol_id", { "protocol_id": value, .. }) =>
Some(decode_json_string(value, field))
("from", { "from": value, .. }) => Some(decode_json_string(value, field))
("to", { "to": value, .. }) => Some(decode_json_string(value, field))
("label", { "label": value, .. }) => Some(decode_json_string(value, field))
("object_id", { "object_id": value, .. }) =>
Some(decode_json_string(value, field))
_ => None
}
}
///|
fn decode_json_string(
json : Json,
field : String,
) -> String raise ProtocolIngestError {
@json.from_json(json) catch {
_ => raise ProtocolIngestError::InvalidField(field, json.stringify())
}
}
///|
fn clean_value(value : String) -> String {
let trimmed = value[:].trim().to_owned()
let without_comma = if trimmed.has_suffix(",") {
trimmed[:trimmed.length() - 1].to_owned()
} else {
trimmed
}
if without_comma.length() >= 2 &&
(
(without_comma.has_prefix("\"") && without_comma.has_suffix("\"")) ||
(without_comma.has_prefix("'") && without_comma.has_suffix("'"))
) {
without_comma[1:without_comma.length() - 1].to_owned()
} else {
without_comma
}
}
///|
fn normalized(value : String) -> String {
clean_value(value).to_lower()
}