#![forbid(unsafe_code)]
use serde::{Deserialize, Serialize};
use std::collections::BTreeMap;
use std::io::{self, Write};
pub const UTF8_BOM: char = '\u{feff}';
#[must_use]
pub fn strip_utf8_bom(s: &str) -> &str {
s.strip_prefix(UTF8_BOM).unwrap_or(s)
}
pub fn write_json_line<W: Write, T: Serialize>(mut w: W, value: &T) -> io::Result<()> {
let s = serde_json::to_string(value).map_err(io::Error::other)?;
w.write_all(s.as_bytes())?;
w.write_all(b"\n")?;
w.flush()?;
Ok(())
}
pub fn print_json_line<T: Serialize>(value: &T) -> io::Result<()> {
let stdout = io::stdout();
let mut handle = stdout.lock();
write_json_line(&mut handle, value)
}
pub fn print_json_line_stderr<T: Serialize>(value: &T) -> io::Result<()> {
let stderr = io::stderr();
let mut handle = stderr.lock();
match write_json_line(&mut handle, value) {
Ok(()) => Ok(()),
Err(e) if e.kind() == io::ErrorKind::BrokenPipe => Ok(()),
Err(e) => Err(e),
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct ErrorEnvelope {
pub exit_code: i32,
#[serde(default)]
pub error_code: String,
pub message: String,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub remote_exit_code: Option<i32>,
pub error_class: crate::errors::ErrorClass,
pub retryable: bool,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub suggestion: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SuccessEnvelope {
pub ok: bool,
pub event: String,
#[serde(flatten)]
pub fields: BTreeMap<String, serde_json::Value>,
}
impl SuccessEnvelope {
#[must_use]
pub fn new(event: impl Into<String>, fields: BTreeMap<String, serde_json::Value>) -> Self {
Self {
ok: true,
event: event.into(),
fields,
}
}
#[must_use]
pub fn from_value(event: &str, fields: serde_json::Value) -> Self {
let mut map = BTreeMap::new();
match fields {
serde_json::Value::Object(obj) => {
for (k, v) in obj {
map.insert(k, v);
}
}
other => {
map.insert("data".into(), other);
}
}
Self::new(event, map)
}
}