Shared Rust kernels
catalogue/text_format
All modules · quoted at commit 9e1884369a67 of DnaCalc/DnaCalc. Comments are the source's own.
read_value · 26 lines · calc-func/src/catalogue/text_format.rs on GitHub/// Text argument `index` read as VALUE reads it under the call's locale profile
/// ([`crate::value_text`]): #VALUE! for text the grammar does not read and for a value that is
/// not text; an error is the result; refused without a profile.
pub(super) fn read_value(site: &Site<'_>, a: &[CalcValue], index: usize) -> Step<Reading> {
match a[index].core_ref() {
CoreRef::Text(text) => {
let environment = site.environment().ok_or_else(|| site.not_modelled(None))?;
let profile = environment
.profile
.as_ref()
.ok_or_else(|| site.not_modelled(None))?;
let context = ReadContext {
profile,
date_system: environment.date_system,
now: environment.now,
};
value_text::read(text.utf16_code_units(), &context).map_err(|unread| match unread {
Unread::Rejected => Exit::Error(WorksheetErrorCode::Value),
Unread::NotModelled => site.not_modelled(Some(index)),
})
}
CoreRef::Error(code) => fail(code),
_ if a[index].callable_value().is_some() => Err(site.not_modelled(Some(index))),
_ => fail(WorksheetErrorCode::Value),
}
}
Used by 14 functions.
value · 21 lines · calc-func/src/catalogue/text_format.rs on GitHub/// VALUE: a number as it is, a blank as 0, a logical #VALUE!, text read as number text, a date
/// or a time.
fn value(site: &Site<'_>, a: &[CalcValue]) -> Step<CalcValue> {
match a[0].core_ref() {
CoreRef::Number(_) | CoreRef::Empty | CoreRef::Missing => {
site.number(site.number_argument(0, &a[0])?)
}
CoreRef::Logical(_) => fail(WorksheetErrorCode::Value),
CoreRef::Text(text) => {
if text.len_utf16_code_units() > MAX_READER_TEXT {
return fail(WorksheetErrorCode::Value);
}
if let Ok(Numeric::Decimal(decimal)) = coerce::number(&a[0]) {
return site.number(site.argument(0, decimal.nearest())?);
}
let reading = read_value(site, a, 0)?;
site.number(reading.value())
}
_ => read_value(site, a, 0).and_then(|reading| site.number(reading.value())),
}
}
Used by 12 functions.
profile · 9 lines · calc-func/src/catalogue/text_format.rs on GitHub/// The call's environment and its locale profile, or the refusal of a call without them.
fn profile<'a>(site: &'a Site<'_>) -> Step<(&'a CalcEnvironment, &'a FormatProfile)> {
let environment = site.environment().ok_or_else(|| site.not_modelled(None))?;
let profile = environment
.profile
.as_ref()
.ok_or_else(|| site.not_modelled(None))?;
Ok((environment, profile))
}
Used by 21 functions.
rendered · 7 lines · calc-func/src/catalogue/text_format.rs on GitHubfn rendered(site: &Site<'_>, result: Result<String, Failure>) -> Step<CalcValue> {
match result {
Ok(text) => Ok(crate::catalogue::kit::text_value(&text)),
Err(Failure::Value) => fail(WorksheetErrorCode::Value),
Err(Failure::NotModelled) => Err(site.not_modelled(None)),
}
}
Used by 18 functions.
text · 56 lines · calc-func/src/catalogue/text_format.rs on GitHub/// TEXT: a number (or text that reads as one) rendered with the code, other text through the
/// code's text section.
fn text(site: &Site<'_>, a: &[CalcValue]) -> Step<CalcValue> {
if a[0].is_excel_overflow() {
let code = text_arg(site, a, 1)?.to_string_lossy();
if code.eq_ignore_ascii_case("general") || code == "@" {
let default = crate::locale::format_profile(LocaleId::EnZa);
let profile = site.environment().and_then(|e| e.profile.as_ref()).unwrap_or(&default);
return Ok(crate::catalogue::kit::text_value(&format::general(
profile, a[0].as_number().expect("qualified overflow number"),
)));
}
return fail(WorksheetErrorCode::Value);
}
if let (Some(environment), CoreRef::Number(_), CoreRef::Text(code)) =
(site.environment(), a[0].core_ref(), a[1].core_ref())
{
if environment.profile.is_none() {
let number = site.number_argument(0, &a[0])?;
return rendered(site, format::text_declared_date_locale(
environment.date_system, number, &code.to_string_lossy(),
));
}
}
let (environment, profile) = profile(site)?;
let code = text_arg(site, a, 1)?.to_string_lossy();
let number = match a[0].core_ref() {
CoreRef::Number(_) | CoreRef::Empty | CoreRef::Missing => {
site.number_argument(0, &a[0])?
}
CoreRef::Error(code) => return fail(code),
CoreRef::Logical(_) | CoreRef::Text(_) => {
let as_number = if matches!(a[0].core_ref(), CoreRef::Text(_)) {
match site.number_argument(0, &a[0]) {
Ok(n) => Some(n),
Err(Exit::Error(WorksheetErrorCode::Value)) => None,
Err(other) => return Err(other),
}
} else {
None
};
match as_number {
Some(n) => n,
None => {
let shown = text_arg(site, a, 0)?.to_string_lossy();
return rendered(site, format::text_of_text(&shown, &code, profile));
}
}
}
_ => return Err(site.not_modelled(Some(0))),
};
rendered(
site,
format::text(profile, environment.date_system, number, &code),
)
}
Used by 18 functions.
places · 11 lines · calc-func/src/catalogue/text_format.rs on GitHub/// The places of FIXED and DOLLAR: 2 when the call leaves them out; beyond 127 is #VALUE!.
fn places(site: &Site<'_>, a: &[CalcValue]) -> Step<i32> {
if a.len() < 2 || a[1].is_missing() {
return Ok(2);
}
let places = num(site, a, 1)?.trunc();
if places > 127.0 {
return fail(WorksheetErrorCode::Value);
}
Ok(places.max(-400.0) as i32)
}
Used by 2 functions.
given · 4 lines · calc-func/src/catalogue/text_format.rs on GitHub/// An optional argument: `None` when the call does not reach it or leaves it empty.
fn given(a: &[CalcValue], index: usize) -> bool {
a.get(index).is_some_and(|value| !value.is_missing())
}
Used by 9 functions.
binary_flag · 15 lines · calc-func/src/catalogue/text_format.rs on GitHub/// A 0-or-1 flag argument (match modes, formats): another number is #VALUE!.
fn binary_flag(site: &Site<'_>, a: &[CalcValue], index: usize) -> Step<bool> {
if !given(a, index) {
return Ok(false);
}
let n = num(site, a, index)?;
if !n.is_finite() {
return fail(WorksheetErrorCode::Value);
}
match n.trunc() {
0.0 => Ok(false),
1.0 => Ok(true),
_ => fail(WorksheetErrorCode::Value),
}
}
Used by 8 functions.
delimiters · 18 lines · calc-func/src/catalogue/text_format.rs on GitHub/// The texts a delimiter argument gives: a single text, or each cell of an array or reference.
fn delimiters(
site: &Site<'_>,
a: &[CalcValue],
index: usize,
sv: &mut EvalServices<'_>,
) -> Step<Vec<ExcelText>> {
let g = grid(site, a, index, sv)?;
let mut out = Vec::with_capacity(g.cells.len());
for value in &g.cells {
if matches!(value.core_ref(), CoreRef::Array(_) | CoreRef::Reference(_)) {
return Err(site.not_modelled(Some(index)));
}
let units = site.argument(index, coerce::text(value))?;
out.push(ExcelText::from_utf16_code_units(units.into_owned()));
}
Ok(out)
}
Used by 3 functions.
text_delim · 32 lines · calc-func/src/catalogue/text_format.rs on GitHubfn text_delim(site: &Site<'_>, a: &[CalcValue], sv: &mut EvalServices<'_>, side: Side) -> Step<CalcValue> {
let text = text_arg(site, a, 0)?;
let delimiters = delimiters(site, a, 1, sv)?;
let instance = if given(a, 2) {
let n = num(site, a, 2)?;
if !n.is_finite() || n.trunc().abs() > 1e9 {
return fail(WorksheetErrorCode::Value);
}
n.trunc() as isize
} else {
1
};
let case_insensitive = binary_flag(site, a, 3)?;
let match_end = binary_flag(site, a, 4)?;
// fn1 Excel 16.0.20527: an explicitly supplied instance is validated against the empty
// input before fallback/match_end. An empty delimiter still returns empty text.
if given(a, 2) && text.len_utf16_code_units() == 0
&& !delimiters.iter().any(|d| d.len_utf16_code_units() == 0)
{
return fail(WorksheetErrorCode::Value);
}
match k::delimited(side, &text, &delimiters, instance, case_insensitive, match_end) {
Delimited::Text(found) => Ok(CalcValue::text(found)),
Delimited::BadInstance => fail(WorksheetErrorCode::Value),
Delimited::NotFound if a.len() > 5 => Ok(if a[5].is_missing() {
CalcValue::error(WorksheetErrorCode::NA)
} else {
a[5].clone()
}),
Delimited::NotFound => fail(WorksheetErrorCode::NA),
}
}
Used by 2 functions.
value_text · 14 lines · calc-func/src/catalogue/text_format.rs on GitHub/// A value's text as VALUETOTEXT and ARRAYTOTEXT write it, concise or strict (text quoted).
fn value_text(site: &Site<'_>, index: usize, value: &CalcValue, strict: bool) -> Step<String> {
Ok(match value.core_ref() {
CoreRef::Number(_) => {
String::from_utf16_lossy(&site.argument(index, coerce::text(value))?)
}
CoreRef::Text(t) if strict => format!("\"{}\"", t.to_string_lossy().replace('"', "\"\"")),
CoreRef::Text(t) => t.to_string_lossy(),
CoreRef::Logical(b) => if b { "TRUE" } else { "FALSE" }.to_owned(),
CoreRef::Error(code) => k::error_literal(code).to_owned(),
CoreRef::Empty | CoreRef::Missing => String::new(),
CoreRef::Array(_) | CoreRef::Reference(_) => return Err(site.not_modelled(Some(index))),
})
}
Used by 2 functions.
case_sensitive · 4 lines · calc-func/src/catalogue/text_format.rs on GitHub/// The case sensitivity of the REGEX functions: 0 (the default) case-sensitive, 1 not.
fn case_sensitive(site: &Site<'_>, a: &[CalcValue], index: usize) -> Step<bool> {
Ok(!binary_flag(site, a, index)?)
}
Used by 3 functions.
regex · 18 lines · calc-func/src/catalogue/text_format.rs on GitHubfn regex(
site: &Site<'_>,
text: &ExcelText,
pattern: &ExcelText,
case_sensitive: bool,
first_only: bool,
) -> Step<(Vec<char>, Vec<(usize, usize)>)> {
if !tk::is_well_formed(text) || !tk::is_well_formed(pattern) {
return Err(site.not_modelled(None));
}
k::regex_matches(
&text.to_string_lossy(),
&pattern.to_string_lossy(),
case_sensitive,
first_only,
)
.map_err(|_| site.not_modelled(Some(1)))
}
Used by 3 functions.
width_conversion · 14 lines · calc-func/src/catalogue/text_format.rs on GitHub/// ASC and DBCS change character widths only under a double-byte language; under every other
/// profile Excel gives the text as it is (Excel 16.0.20527 under en-ZA, the catalogue's sheet
/// dczsn3_bytes: full-width and half-width forms, kana, CJK and Hangul are kept). Under the
/// ja-JP, ko-KR and zh-CN profiles they are refused (bead dc-zsn.3).
fn width_conversion(site: &Site<'_>, a: &[CalcValue]) -> Step<CalcValue> {
let (_, profile) = profile(site)?;
if matches!(
profile.id,
LocaleId::JaJp | LocaleId::KoKr | LocaleId::ZhCn
) {
return Err(site.not_modelled(None));
}
Ok(CalcValue::text(text_arg(site, a, 0)?))
}
Used by 2 functions.