// A part: one piece of a message or an artifact. Exactly one of text, raw bytes, a URL or a JSON
// value carries the content; `filename`, `media_type` and `metadata` describe it.
///|
/// What a part carries (A2A `Part.content` oneof).
#derive(Eq)
pub(all) enum PartContent {
/// Plain text.
Text(String)
/// Bytes carried inline, base64 on the wire.
Raw(bytes~ : Bytes, filename~ : String?, media_type~ : String?)
/// A URL pointing at the content.
Url(url~ : String, filename~ : String?, media_type~ : String?)
/// A structured JSON value.
Data(Json)
}
///|
/// One piece of a message or an artifact (A2A `Part`).
#derive(Eq)
pub(all) struct Part {
content : PartContent
metadata : Json?
}
///|
/// A text part.
pub fn Part::text(text : String) -> Part {
{ content: Text(text), metadata: None, }
}
///|
/// A part carrying bytes inline.
pub fn Part::bytes(
bytes : Bytes,
filename? : String,
media_type? : String,
) -> Part {
{ content: Raw(bytes~, filename~, media_type~), metadata: None, }
}
///|
/// A part pointing at content by URL.
pub fn Part::uri(
url : String,
filename? : String,
media_type? : String,
) -> Part {
{ content: Url(url~, filename~, media_type~), metadata: None, }
}
///|
/// A part carrying a structured JSON value.
pub fn Part::data(data : Json) -> Part {
{ content: Data(data), metadata: None, }
}
///|
/// The same part with metadata attached.
pub fn Part::with_metadata(self : Part, metadata : Json) -> Part {
{ ..self, metadata: Some(metadata), }
}
///|
/// The text this part carries, when it is a text part.
pub fn Part::text_of(self : Part) -> String? {
match self.content {
Text(text) => Some(text)
_ => None
}
}
///|
/// The media type this part declares, when it declares one.
pub fn Part::media_type(self : Part) -> String? {
match self.content {
Raw(media_type~, ..) | Url(media_type~, ..) => media_type
Text(_) | Data(_) => None
}
}
///|
pub impl @json.ToJson for Part with fn to_json(self) {
let fields = object()
match self.content {
Text(text) => fields["text"] = Json::string(text)
Raw(bytes~, ..) =>
fields["raw"] = Json::string(@base64.encode(bytes[:], padding=true))
Url(url~, ..) => fields["url"] = Json::string(url)
Data(data) => fields["data"] = data
}
match self.content {
Raw(filename~, media_type~, ..) | Url(filename~, media_type~, ..) => {
put_str(fields, "filename", filename)
put_str(fields, "mediaType", media_type)
}
Text(_) | Data(_) => ()
}
put_json(fields, "metadata", self.metadata)
Json::object(fields)
}
///|
pub impl @json.FromJson for Part with fn from_json(json, path) {
let fields = as_object(json, path)
// The oneof is read in proto field order: a part naming more than one content kind keeps the
// first, which is what a reader of the schema expects rather than an error.
let content = if fields.get("text") is Some(String(text)) {
Text(text)
} else if fields.get("raw") is Some(String(encoded)) {
Raw(
bytes=@base64.decode(encoded) catch {
_ => raise @json.JsonDecodeError((path.add_key("raw"), "want base64"))
},
filename=opt_str(fields, "filename"),
media_type=opt_str(fields, "mediaType"),
)
} else if fields.get("url") is Some(String(url)) {
Url(
url~,
filename=opt_str(fields, "filename"),
media_type=opt_str(fields, "mediaType"),
)
} else if fields.get("data") is Some(data) {
Data(data)
} else {
raise @json.JsonDecodeError(
(path, "want a part with text, raw, url or data"),
)
}
{ content, metadata: fields.get("metadata"), }
}
///|
pub extend Part with @json.ToJson::{to_json}
///|
pub extend Part with @json.FromJson::{from_json}
///|
pub extend PartContent with Eq::{not_equal, equal}
///|
pub extend Part with Eq::{not_equal, equal}