use std::borrow::Cow;
use std::io::{self, Write};
use crate::header::FixedPointGain;
use crate::{escaping, Error, FIELD_NAME_TERMINATOR};
pub trait CommentList {
type Iter<'a>: Iterator<Item = (&'a str, &'a str)>
where
Self: 'a;
fn len(&self) -> usize;
fn is_empty(&self) -> bool { self.len() == 0 }
fn clear(&mut self);
fn get_first(&self, key: &str) -> Option<&str>;
fn replace(&mut self, key: &str, value: &str) -> Result<(), Error>;
fn remove_all(&mut self, key: &str);
fn push(&mut self, key: &str, value: &str) -> Result<(), Error>;
fn iter(&self) -> Self::Iter<'_>;
fn retain<F: FnMut(&str, &str) -> bool>(&mut self, f: F);
fn write_as_text<W: Write>(&self, mut writer: W, escape: bool) -> Result<(), io::Error> {
for (k, v) in self.iter() {
let v = if escape { escaping::escape_str(v) } else { Cow::from(v) };
writeln!(writer, "{}{}{}", k, FIELD_NAME_TERMINATOR as char, v)?;
}
Ok(())
}
fn extend<K, V, I>(&mut self, comments: I) -> Result<(), Error>
where
K: AsRef<str>,
V: AsRef<str>,
I: IntoIterator<Item = (K, V)>,
{
let comments = comments.into_iter();
for (key, value) in comments {
let (key, value) = (key.as_ref(), value.as_ref());
self.push(key, value)?;
}
Ok(())
}
fn get_gain_from_tag(&self, tag: &str) -> Result<Option<FixedPointGain>, Error> {
let parsed =
self.get_first(tag).map(|v| v.parse::<FixedPointGain>().map_err(|_| Error::InvalidR128Tag(v.into())));
match parsed {
Some(Ok(v)) => Ok(Some(v)),
Some(Err(e)) => Err(e),
None => Ok(None),
}
}
fn set_tag_to_gain(&mut self, tag: &str, gain: FixedPointGain) -> Result<(), Error> {
self.replace(tag, &format!("{}", gain.as_fixed_point()))
}
}
pub fn parse_comment(comment: &str) -> Result<(&str, &str), Error> {
let offset = comment.find(char::from(FIELD_NAME_TERMINATOR)).ok_or(Error::MissingCommentSeparator)?;
let (key, value) = comment.split_at(offset);
validate_comment_field_name(key)?;
Ok((key, &value[1..]))
}
pub fn validate_comment_field_name(field_name: &str) -> Result<(), Error> {
for c in field_name.chars() {
match c {
' '..='<' | '>'..='}' => {}
_ => return Err(Error::InvalidOpusCommentFieldName(field_name.into())),
}
}
Ok(())
}