use lindera::dictionary::Metadata;
use lindera::space_penalty::SpacePenaltyConfig;
use crate::schema::CoreSchema;
const DEFAULT_NAME: &str = "default";
const DEFAULT_ENCODING: &str = "UTF-8";
const DEFAULT_WORD_COST: i16 = -10000;
const DEFAULT_LEFT_CONTEXT_ID: u16 = 1288;
const DEFAULT_RIGHT_CONTEXT_ID: u16 = 1288;
const DEFAULT_FIELD_VALUE: &str = "*";
fn default_user_dictionary_schema() -> CoreSchema {
CoreSchema::new(vec![
"surface".to_string(),
"reading".to_string(),
"pronunciation".to_string(),
])
}
#[derive(Debug, Clone)]
pub struct CoreMetadata {
pub name: String,
pub encoding: String,
pub default_word_cost: i16,
pub default_left_context_id: u16,
pub default_right_context_id: u16,
pub default_field_value: String,
pub flexible_csv: bool,
pub skip_invalid_cost_or_id: bool,
pub normalize_details: bool,
pub dictionary_schema: CoreSchema,
pub user_dictionary_schema: CoreSchema,
pub space_penalty: Option<SpacePenaltyConfig>,
}
impl CoreMetadata {
#[allow(clippy::too_many_arguments)]
pub fn new(
name: Option<String>,
encoding: Option<String>,
default_word_cost: Option<i16>,
default_left_context_id: Option<u16>,
default_right_context_id: Option<u16>,
default_field_value: Option<String>,
flexible_csv: Option<bool>,
skip_invalid_cost_or_id: Option<bool>,
normalize_details: Option<bool>,
dictionary_schema: Option<CoreSchema>,
user_dictionary_schema: Option<CoreSchema>,
) -> Self {
Self {
name: name.unwrap_or_else(|| DEFAULT_NAME.to_string()),
encoding: encoding.unwrap_or_else(|| DEFAULT_ENCODING.to_string()),
default_word_cost: default_word_cost.unwrap_or(DEFAULT_WORD_COST),
default_left_context_id: default_left_context_id.unwrap_or(DEFAULT_LEFT_CONTEXT_ID),
default_right_context_id: default_right_context_id.unwrap_or(DEFAULT_RIGHT_CONTEXT_ID),
default_field_value: default_field_value
.unwrap_or_else(|| DEFAULT_FIELD_VALUE.to_string()),
flexible_csv: flexible_csv.unwrap_or(false),
skip_invalid_cost_or_id: skip_invalid_cost_or_id.unwrap_or(false),
normalize_details: normalize_details.unwrap_or(false),
dictionary_schema: dictionary_schema.unwrap_or_else(CoreSchema::create_default),
user_dictionary_schema: user_dictionary_schema
.unwrap_or_else(default_user_dictionary_schema),
space_penalty: None,
}
}
pub fn create_default() -> Self {
Self::new(
None, None, None, None, None, None, None, None, None, None, None,
)
}
}
impl CoreMetadata {
pub fn space_penalty_json(&self) -> Option<String> {
self.space_penalty
.as_ref()
.and_then(|config| serde_json::to_string(config).ok())
}
}
impl Default for CoreMetadata {
fn default() -> Self {
Self::create_default()
}
}
impl From<Metadata> for CoreMetadata {
fn from(metadata: Metadata) -> Self {
Self {
name: metadata.name,
encoding: metadata.encoding,
default_word_cost: metadata.default_word_cost,
default_left_context_id: metadata.default_left_context_id,
default_right_context_id: metadata.default_right_context_id,
default_field_value: metadata.default_field_value,
flexible_csv: metadata.flexible_csv,
skip_invalid_cost_or_id: metadata.skip_invalid_cost_or_id,
normalize_details: metadata.normalize_details,
dictionary_schema: metadata.dictionary_schema.into(),
user_dictionary_schema: metadata.user_dictionary_schema.into(),
space_penalty: metadata.space_penalty,
}
}
}
impl From<CoreMetadata> for Metadata {
fn from(metadata: CoreMetadata) -> Self {
let space_penalty = metadata.space_penalty;
let mut converted = Metadata::new(
metadata.name,
metadata.encoding,
metadata.default_word_cost,
metadata.default_left_context_id,
metadata.default_right_context_id,
metadata.default_field_value,
metadata.flexible_csv,
metadata.skip_invalid_cost_or_id,
metadata.normalize_details,
metadata.dictionary_schema.into(),
metadata.user_dictionary_schema.into(),
);
converted.space_penalty = space_penalty;
converted
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn create_default_uses_binding_defaults() {
let m = CoreMetadata::create_default();
assert_eq!(m.name, "default");
assert_eq!(m.encoding, "UTF-8");
assert_eq!(m.default_word_cost, -10000);
assert_eq!(m.default_left_context_id, 1288);
assert_eq!(m.default_right_context_id, 1288);
assert_eq!(m.default_field_value, "*");
assert!(!m.flexible_csv);
assert!(!m.skip_invalid_cost_or_id);
assert!(!m.normalize_details);
assert_eq!(m.dictionary_schema.field_count(), 13);
assert_eq!(m.dictionary_schema.fields()[5], "pos_detail_1");
assert_eq!(m.user_dictionary_schema.fields().len(), 3);
assert_eq!(m.user_dictionary_schema.fields()[1], "reading");
}
#[test]
fn new_applies_overrides() {
let m = CoreMetadata::new(
Some("custom".to_string()),
Some("EUC-JP".to_string()),
Some(-5000),
Some(100),
Some(200),
Some("N/A".to_string()),
Some(true),
Some(true),
Some(true),
None,
None,
);
assert_eq!(m.name, "custom");
assert_eq!(m.encoding, "EUC-JP");
assert_eq!(m.default_word_cost, -5000);
assert_eq!(m.default_left_context_id, 100);
assert_eq!(m.default_right_context_id, 200);
assert_eq!(m.default_field_value, "N/A");
assert!(m.flexible_csv && m.skip_invalid_cost_or_id && m.normalize_details);
}
#[test]
fn converts_to_and_from_lindera() {
let m = CoreMetadata::create_default();
let lindera: Metadata = m.into();
assert_eq!(lindera.name, "default");
assert_eq!(lindera.default_left_context_id, 1288);
assert_eq!(lindera.user_dictionary_schema.field_count(), 3);
let back: CoreMetadata = lindera.into();
assert_eq!(back.name, "default");
assert_eq!(back.dictionary_schema.fields()[5], "pos_detail_1");
assert!(back.space_penalty.is_none());
assert!(back.space_penalty_json().is_none());
}
#[test]
fn keeps_space_penalty_from_ko_dic_metadata() {
let path = std::path::Path::new(env!("CARGO_MANIFEST_DIR"))
.join("../lindera-ko-dic/metadata.json");
let json = std::fs::read_to_string(&path).unwrap();
let metadata: Metadata = serde_json::from_str(&json).unwrap();
let shipped = metadata.space_penalty.clone().unwrap();
assert!(!shipped.rules.is_empty());
let core = CoreMetadata::from(metadata);
assert_eq!(core.space_penalty.as_ref(), Some(&shipped));
let as_json = core.space_penalty_json().unwrap();
assert!(as_json.contains("\"rules\""));
assert!(as_json.contains("JKS"));
let rebuilt: Metadata = core.into();
assert_eq!(rebuilt.space_penalty, Some(shipped));
}
}