pub(crate) struct JoinStringsOptions<'a> {
pub(crate) separator: &'a str,
pub(crate) first_separator: Option<&'a str>,
pub(crate) last_separator: Option<&'a str>,
pub(crate) output_length_limit: Option<usize>,
}
impl Default for JoinStringsOptions<'_> {
fn default() -> Self {
Self {
separator: ", ",
first_separator: None,
last_separator: Some(" and "),
output_length_limit: None,
}
}
}
pub(crate) fn join_strings(
mut iter: impl Iterator<Item = impl AsRef<str>>,
options: JoinStringsOptions,
) -> String {
let mut output = String::new();
let Some(first) = iter.next() else {
return output;
};
output.push_str(first.as_ref());
let Some(second) = iter.next() else {
return output;
};
let mut element_length = first.as_ref().chars().count();
let mut push_sep_and_element = |sep: &str, element: &str| {
if let Some(output_length_limit) = options.output_length_limit {
let new_element_length = element_length + element.chars().count();
return if new_element_length <= output_length_limit {
element_length = new_element_length;
output.push_str(sep);
output.push_str(element);
false
} else {
output.push_str(options.separator);
output.push_str("...");
true
};
}
output.push_str(sep);
output.push_str(element);
false
};
let last_sep = options.last_separator.unwrap_or(options.separator);
let Some(mut current) = iter.next() else {
push_sep_and_element(options.first_separator.unwrap_or(last_sep), second.as_ref());
return output;
};
if push_sep_and_element(
options.first_separator.unwrap_or(options.separator),
second.as_ref(),
) {
return output;
}
for next in iter {
if push_sep_and_element(options.separator, current.as_ref()) {
return output;
}
current = next;
}
push_sep_and_element(last_sep, current.as_ref());
output
}
pub(crate) struct HumanReadableListOptions<'a> {
pub(crate) prefix: Option<HumanReadableListPrefix<'a>>,
pub(crate) last_separator: Option<&'a str>,
pub(crate) output_length_limit: usize,
pub(crate) empty_output: &'a str,
}
pub(crate) struct HumanReadableListPrefix<'a> {
pub(crate) singular: &'a str,
pub(crate) plural: &'a str,
}
impl Default for HumanReadableListOptions<'_> {
fn default() -> Self {
Self {
prefix: None,
last_separator: Some(" and "),
output_length_limit: 100,
empty_output: "",
}
}
}
pub(crate) fn human_readable_list(
mut iter: impl Iterator<Item = impl AsRef<str>>,
options: HumanReadableListOptions,
) -> String {
let Some(first) = iter.next() else {
return options.empty_output.to_owned();
};
let Some(second) = iter.next() else {
return if let Some(prefix) = options.prefix {
format!("{} {}", prefix.singular, first.as_ref())
} else {
first.as_ref().to_owned()
};
};
let joined_strings = join_strings(
[first, second].into_iter().chain(iter),
JoinStringsOptions {
last_separator: options.last_separator,
output_length_limit: Some(options.output_length_limit),
..Default::default()
},
);
if let Some(prefix) = options.prefix {
format!("{} {}", prefix.plural, joined_strings)
} else {
joined_strings
}
}
pub(crate) fn human_readable_subgraph_names(
subgraph_names: impl Iterator<Item = impl AsRef<str>>,
) -> String {
human_readable_list(
subgraph_names.map(|name| format!("\"{}\"", name.as_ref())),
HumanReadableListOptions {
prefix: Some(HumanReadableListPrefix {
singular: "subgraph",
plural: "subgraphs",
}),
..Default::default()
},
)
}
pub(crate) fn human_readable_types(types: impl Iterator<Item = impl AsRef<str>>) -> String {
human_readable_list(
types.map(|t| format!("\"{}\"", t.as_ref())),
HumanReadableListOptions {
prefix: Some(HumanReadableListPrefix {
singular: "type",
plural: "types",
}),
..Default::default()
},
)
}