use serde::Deserialize;
use crate::pre_tokenized::PreTokenizedString;
use crate::pre_tokenized::PtSplit;
use super::Error;
#[derive(Deserialize)]
struct MetaspaceRaw {
#[serde(default = "String::new")]
replacement: String,
#[serde(default)]
prepend_scheme: Option<String>,
#[serde(default)]
add_prefix_space: bool,
}
#[derive(Clone, Debug, Deserialize)]
#[serde(try_from = "MetaspaceRaw")]
pub struct Metaspace {
replacement: char,
prepend_scheme: PrependScheme,
add_prefix_space: bool,
}
#[derive(Clone, Debug, PartialEq, Eq)]
enum PrependScheme {
Never,
Always,
First,
}
impl Metaspace {
pub fn pre_tokenize(&self, pts: &mut PreTokenizedString) -> Result<(), Error> {
let buffer = pts.buffer.as_str();
let old_splits = &pts.splits;
let mut new_buf = String::with_capacity(buffer.len());
let mut new_splits = Vec::with_capacity(old_splits.len() << 1);
let prepend_first = matches!(
self.prepend_scheme,
PrependScheme::Always | PrependScheme::First
);
let mut is_first_text = prepend_first;
for split in old_splits {
if split.token_id.is_some() {
let start = new_buf.len();
new_buf.push_str(&buffer[split.range.clone()]);
let end = new_buf.len();
new_splits.push(PtSplit {
range: start..end,
token_id: split.token_id,
});
continue;
}
let text = &buffer[split.range.clone()];
if text.is_empty() {
continue;
}
let mut prev_was_space = false;
for (_i, ch) in text.char_indices() {
if ch.is_whitespace() {
prev_was_space = true;
continue;
}
if prev_was_space || (is_first_text && self.add_prefix_space) {
let start = new_buf.len();
new_buf.push(self.replacement);
new_buf.push(ch);
let end = new_buf.len();
new_splits.push(PtSplit {
range: start..end,
token_id: None,
});
is_first_text = false;
prev_was_space = false;
continue;
}
let c_start = new_buf.len();
new_buf.push(ch);
let c_end = new_buf.len();
if let Some(last) = new_splits.last_mut()
&& last.token_id.is_none()
{
last.range.end = c_end;
} else {
new_splits.push(PtSplit {
range: c_start..c_end,
token_id: None,
});
}
is_first_text = false;
prev_was_space = false;
}
if prev_was_space {
let start = new_buf.len();
new_buf.push(self.replacement);
new_splits.push(PtSplit {
range: start..new_buf.len(),
token_id: None,
});
}
}
pts.buffer = new_buf;
pts.splits = new_splits;
Ok(())
}
}
impl TryFrom<MetaspaceRaw> for Metaspace {
type Error = Error;
fn try_from(raw: MetaspaceRaw) -> Result<Self, Error> {
let replacement = if raw.replacement.is_empty() {
'\u{2581}' } else {
raw.replacement
.chars()
.next()
.ok_or_else(|| Error::Json(serde::de::Error::custom("empty replacement")))?
};
let prepend_scheme = match raw.prepend_scheme.as_deref() {
None | Some("first") => PrependScheme::First,
Some("always") => PrependScheme::Always,
Some("never") => PrependScheme::Never,
Some(other) => {
return Err(Error::Unsupported(format!(
"unknown prepend_scheme: {other}"
)));
}
};
Ok(Self {
replacement,
prepend_scheme,
add_prefix_space: raw.add_prefix_space,
})
}
}