pub mod android_strings;
pub mod csv;
pub mod strings;
pub mod tsv;
pub mod xcstrings;
pub mod xliff;
use std::{
fmt::{Display, Formatter},
str::FromStr,
};
pub use android_strings::Format as AndroidStringsFormat;
pub use csv::{Format as CSVFormat, MultiLanguageCSVRecord};
pub use strings::Format as StringsFormat;
pub use tsv::{Format as TSVFormat, MultiLanguageTSVRecord};
pub use xcstrings::Format as XcstringsFormat;
pub use xliff::Format as XliffFormat;
use crate::Error;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum FormatType {
AndroidStrings(Option<String>),
Strings(Option<String>),
Xcstrings,
Xliff(Option<String>),
CSV,
TSV,
}
impl Display for FormatType {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
FormatType::AndroidStrings(_) => write!(f, "android"),
FormatType::Strings(_) => write!(f, "strings"),
FormatType::Xcstrings => write!(f, "xcstrings"),
FormatType::Xliff(_) => write!(f, "xliff"),
FormatType::CSV => write!(f, "csv"),
FormatType::TSV => write!(f, "tsv"),
}
}
}
impl FromStr for FormatType {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let s = s.trim().to_ascii_lowercase();
match s.as_str() {
"android" | "androidstrings" | "xml" => Ok(FormatType::AndroidStrings(None)),
"strings" => Ok(FormatType::Strings(None)),
"xcstrings" => Ok(FormatType::Xcstrings),
"xliff" => Ok(FormatType::Xliff(None)),
"csv" => Ok(FormatType::CSV),
"tsv" => Ok(FormatType::TSV),
other => Err(Error::UnknownFormat(other.to_string())),
}
}
}
impl FormatType {
pub fn extension(&self) -> &'static str {
match self {
FormatType::AndroidStrings(_) => "xml",
FormatType::Strings(_) => "strings",
FormatType::Xcstrings => "xcstrings",
FormatType::Xliff(_) => "xliff",
FormatType::CSV => "csv",
FormatType::TSV => "tsv",
}
}
pub fn language(&self) -> Option<&String> {
match self {
FormatType::AndroidStrings(lang) => lang.as_ref(),
FormatType::Strings(lang) => lang.as_ref(),
FormatType::Xcstrings => None,
FormatType::Xliff(lang) => lang.as_ref(),
FormatType::CSV => None,
FormatType::TSV => None,
}
}
pub fn with_language(&self, lang: Option<String>) -> Self {
match self {
FormatType::AndroidStrings(_) => FormatType::AndroidStrings(lang),
FormatType::Strings(_) => FormatType::Strings(lang),
FormatType::Xcstrings => FormatType::Xcstrings,
FormatType::Xliff(_) => FormatType::Xliff(lang),
FormatType::CSV => FormatType::CSV,
FormatType::TSV => FormatType::TSV,
}
}
pub fn matches_language_of(&self, other: &FormatType) -> bool {
match (self, other) {
(FormatType::Xcstrings, _) | (_, FormatType::Xcstrings) => true,
(FormatType::Xliff(_), _) | (_, FormatType::Xliff(_)) => true,
(FormatType::CSV, _) | (_, FormatType::CSV) => true,
(FormatType::TSV, _) | (_, FormatType::TSV) => true,
_ => self.language() == other.language(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_format_type_display() {
assert_eq!(FormatType::AndroidStrings(None).to_string(), "android");
assert_eq!(FormatType::Strings(None).to_string(), "strings");
assert_eq!(FormatType::Xcstrings.to_string(), "xcstrings");
assert_eq!(FormatType::Xliff(None).to_string(), "xliff");
assert_eq!(FormatType::CSV.to_string(), "csv");
assert_eq!(FormatType::TSV.to_string(), "tsv");
}
#[test]
fn test_format_type_from_str() {
assert_eq!(
FormatType::from_str("android").unwrap(),
FormatType::AndroidStrings(None)
);
assert_eq!(
FormatType::from_str("ANDROID").unwrap(),
FormatType::AndroidStrings(None)
);
assert_eq!(
FormatType::from_str("androidstrings").unwrap(),
FormatType::AndroidStrings(None)
);
assert_eq!(
FormatType::from_str("xml").unwrap(),
FormatType::AndroidStrings(None)
);
assert_eq!(
FormatType::from_str("strings").unwrap(),
FormatType::Strings(None)
);
assert_eq!(
FormatType::from_str("STRINGS").unwrap(),
FormatType::Strings(None)
);
assert_eq!(
FormatType::from_str("xcstrings").unwrap(),
FormatType::Xcstrings
);
assert_eq!(
FormatType::from_str("XCSTRINGS").unwrap(),
FormatType::Xcstrings
);
assert_eq!(
FormatType::from_str("xliff").unwrap(),
FormatType::Xliff(None)
);
assert_eq!(
FormatType::from_str("XLIFF").unwrap(),
FormatType::Xliff(None)
);
assert_eq!(FormatType::from_str("csv").unwrap(), FormatType::CSV);
assert_eq!(FormatType::from_str("CSV").unwrap(), FormatType::CSV);
assert_eq!(FormatType::from_str("tsv").unwrap(), FormatType::TSV);
assert_eq!(FormatType::from_str("TSV").unwrap(), FormatType::TSV);
}
#[test]
fn test_format_type_from_str_with_whitespace() {
assert_eq!(
FormatType::from_str(" android ").unwrap(),
FormatType::AndroidStrings(None)
);
assert_eq!(
FormatType::from_str(" strings ").unwrap(),
FormatType::Strings(None)
);
}
#[test]
fn test_format_type_from_str_invalid() {
assert!(FormatType::from_str("invalid").is_err());
assert!(FormatType::from_str("foobar").is_err());
assert!(FormatType::from_str("").is_err());
}
#[test]
fn test_format_type_extension() {
assert_eq!(FormatType::AndroidStrings(None).extension(), "xml");
assert_eq!(FormatType::Strings(None).extension(), "strings");
assert_eq!(FormatType::Xcstrings.extension(), "xcstrings");
assert_eq!(FormatType::CSV.extension(), "csv");
assert_eq!(FormatType::TSV.extension(), "tsv");
}
#[test]
fn test_format_type_language() {
assert_eq!(
FormatType::AndroidStrings(Some("en".to_string())).language(),
Some(&"en".to_string())
);
assert_eq!(
FormatType::Strings(Some("fr".to_string())).language(),
Some(&"fr".to_string())
);
assert_eq!(FormatType::Xcstrings.language(), None);
assert_eq!(FormatType::CSV.language(), None);
assert_eq!(FormatType::TSV.language(), None);
}
#[test]
fn test_format_type_with_language() {
let original = FormatType::AndroidStrings(None);
let with_lang = original.with_language(Some("en".to_string()));
assert_eq!(
with_lang,
FormatType::AndroidStrings(Some("en".to_string()))
);
let original = FormatType::Strings(Some("fr".to_string()));
let without_lang = original.with_language(None);
assert_eq!(without_lang, FormatType::Strings(None));
let original = FormatType::TSV;
let with_lang = original.with_language(Some("fr".to_string()));
assert_eq!(with_lang, FormatType::TSV);
}
#[test]
fn test_format_type_matches_language_of() {
let format1 = FormatType::AndroidStrings(Some("en".to_string()));
let format2 = FormatType::Strings(Some("en".to_string()));
let format3 = FormatType::CSV;
let format4 = FormatType::TSV;
assert!(format1.matches_language_of(&format2));
assert!(format1.matches_language_of(&format3));
assert!(format1.matches_language_of(&format4));
assert!(format1.matches_language_of(&FormatType::Xcstrings));
assert!(FormatType::Xcstrings.matches_language_of(&format1));
}
#[test]
fn test_format_type_matches_language_of_none() {
let format1 = FormatType::AndroidStrings(None);
let format2 = FormatType::Strings(None);
let format3 = FormatType::CSV;
assert!(format1.matches_language_of(&format2));
assert!(format1.matches_language_of(&format3));
}
#[test]
fn test_format_type_debug() {
let format = FormatType::AndroidStrings(Some("en".to_string()));
let debug = format!("{:?}", format);
assert!(debug.contains("AndroidStrings"));
assert!(debug.contains("en"));
}
#[test]
fn test_format_type_clone() {
let original = FormatType::Strings(Some("en".to_string()));
let cloned = original.clone();
assert_eq!(original, cloned);
}
#[test]
fn test_format_type_partial_eq() {
let format1 = FormatType::AndroidStrings(Some("en".to_string()));
let format2 = FormatType::AndroidStrings(Some("en".to_string()));
let format3 = FormatType::AndroidStrings(Some("fr".to_string()));
assert_eq!(format1, format2);
assert_ne!(format1, format3);
}
}