use std::{
borrow::Cow,
cmp::{self, Ordering},
fmt::{self, Display},
};
use sway_types::{SourceEngine, SourceId};
pub(crate) fn get_file_name(
source_engine: &SourceEngine,
source_id: Option<&SourceId>,
) -> Option<String> {
match source_id {
Some(source_id) => source_engine.get_file_name(source_id),
None => None,
}
}
pub(crate) fn number_to_str(number: usize) -> String {
match number {
0 => "zero".to_string(),
1 => "one".to_string(),
2 => "two".to_string(),
3 => "three".to_string(),
4 => "four".to_string(),
5 => "five".to_string(),
6 => "six".to_string(),
7 => "seven".to_string(),
8 => "eight".to_string(),
9 => "nine".to_string(),
10 => "ten".to_string(),
_ => format!("{number}"),
}
}
pub(crate) enum Enclosing {
#[allow(dead_code)]
None,
DoubleQuote,
}
impl Display for Enclosing {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{}",
match self {
Self::None => "",
Self::DoubleQuote => "\"",
},
)
}
}
pub(crate) enum Indent {
#[allow(dead_code)]
None,
Single,
Double,
}
impl Display for Indent {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{}",
match self {
Self::None => "",
Self::Single => " ",
Self::Double => " ",
},
)
}
}
pub(crate) fn sequence_to_str<T>(sequence: &[T], enclosing: Enclosing, max_items: usize) -> String
where
T: Display,
{
sequence_to_str_impl(sequence, enclosing, max_items, "and")
}
pub(crate) fn sequence_to_str_or<T>(
sequence: &[T],
enclosing: Enclosing,
max_items: usize,
) -> String
where
T: Display,
{
sequence_to_str_impl(sequence, enclosing, max_items, "or")
}
fn sequence_to_str_impl<T>(
sequence: &[T],
enclosing: Enclosing,
max_items: usize,
and_or: &str,
) -> String
where
T: Display,
{
assert!(
!sequence.is_empty(),
"Sequence to display must not be empty."
);
assert!(
max_items > 0,
"Maximum number of items to display must be greater than zero."
);
let max_items = cmp::min(max_items, sequence.len());
let (to_display, remaining) = sequence.split_at(max_items);
let fmt_item = |item: &T| format!("{enclosing}{item}{enclosing}");
if !remaining.is_empty() {
format!(
"{}, {} {} more",
to_display
.iter()
.map(fmt_item)
.collect::<Vec<_>>()
.join(", "),
and_or,
number_to_str(remaining.len())
)
} else {
match to_display {
[] => unreachable!("There must be at least one item in the sequence."),
[item] => fmt_item(item),
[first_item, second_item] => {
format!(
"{} {} {}",
fmt_item(first_item),
and_or,
fmt_item(second_item)
)
}
_ => format!(
"{}, {} {}",
to_display
.split_last()
.unwrap()
.1
.iter()
.map(fmt_item)
.collect::<Vec::<_>>()
.join(", "),
and_or,
fmt_item(to_display.last().unwrap())
),
}
}
}
pub(crate) fn sequence_to_list<T>(sequence: &[T], indent: Indent, max_items: usize) -> Vec<String>
where
T: Display,
{
assert!(
!sequence.is_empty(),
"Sequence to display must not be empty."
);
assert!(
max_items > 0,
"Maximum number of items to display must be greater than zero."
);
let mut result = vec![];
let max_items = cmp::min(max_items, sequence.len());
let (to_display, remaining) = sequence.split_at(max_items);
for item in to_display {
result.push(format!("{indent}- {item}"));
}
if !remaining.is_empty() {
result.push(format!(
"{indent}- and {} more",
number_to_str(remaining.len())
));
}
result
}
pub(crate) fn plural_s(count: usize) -> &'static str {
if count == 1 {
""
} else {
"s"
}
}
pub(crate) fn is_are(count: usize) -> &'static str {
if count == 1 {
"is"
} else {
"are"
}
}
pub(crate) fn singular_plural<'a>(count: usize, singular: &'a str, plural: &'a str) -> &'a str {
if count == 1 {
singular
} else {
plural
}
}
pub(crate) fn call_path_suffix_with_args(call_path: &String) -> Cow<String> {
match call_path.rfind(':') {
Some(index) if index < call_path.len() - 1 => {
Cow::Owned(call_path.split_at(index + 1).1.to_string())
}
_ => Cow::Borrowed(call_path),
}
}
pub(crate) fn a_or_an(word: &'static str) -> &'static str {
let is_a = in_definite::is_an(word);
match is_a {
in_definite::Is::An => "an ",
in_definite::Is::A => "a ",
in_definite::Is::None => "",
}
}
pub(crate) fn ascii_sentence_case(text: &String) -> Cow<String> {
if text.is_empty() || text.chars().next().unwrap().is_uppercase() {
Cow::Borrowed(text)
} else {
let mut result = text.clone();
result[0..1].make_ascii_uppercase();
Cow::Owned(result.to_owned())
}
}
pub(crate) fn first_line(text: &str, with_ellipses: bool) -> Cow<str> {
if !text.contains('\n') {
Cow::Borrowed(text)
} else {
let index_of_new_line = text.find('\n').unwrap();
Cow::Owned(text[..index_of_new_line].to_string() + if with_ellipses { "..." } else { "" })
}
}
pub(crate) fn did_you_mean<T, I>(
v: &str,
possible_values: I,
max_num_of_suggestions: usize,
) -> Vec<String>
where
T: AsRef<str>,
I: IntoIterator<Item = T>,
{
let mut candidates: Vec<_> = possible_values
.into_iter()
.map(|pv| (strsim::jaro(v, pv.as_ref()), pv.as_ref().to_owned()))
.filter(|(confidence, _)| *confidence > 0.7)
.collect();
candidates.sort_by(|a, b| b.0.partial_cmp(&a.0).unwrap_or(Ordering::Equal));
candidates
.into_iter()
.take(max_num_of_suggestions)
.map(|(_, pv)| pv)
.collect()
}