use std::borrow::Borrow; use std::cmp::Ordering; use std::convert::Infallible; use std::future::Future; use std::iter; use std::rc::Rc; use std::str::FromStr; use futures::future::join_all; use itertools::{Itertools, chain}; use never::Never; use regex::Regex; use crate::interner::Interner; use crate::{api, match_mapping}; #[derive(Clone, Debug, Hash, PartialEq, Eq)] #[must_use] pub struct FmtUnit { pub subs: Vec, pub variants: Rc, } impl FmtUnit { pub fn new(variants: Rc, subs: impl IntoIterator) -> Self { Self { subs: subs.into_iter().collect(), variants } } pub fn from_api(api: &api::FormattingUnit) -> Self { Self { subs: api.subs.iter().map(Self::from_api).collect(), variants: Rc::new(Variants( (api.variants.iter().map(|var| Variant { bounded: var.bounded, elements: var.elements.iter().map(FmtElement::from_api).collect(), })) .collect(), )), } } pub fn to_api(&self) -> api::FormattingUnit { api::FormattingUnit { subs: self.subs.iter().map(Self::to_api).collect(), variants: (self.variants.0.iter().map(|var| api::FormattingVariant { bounded: var.bounded, elements: var.elements.iter().map(FmtElement::to_api).collect(), })) .collect(), } } pub fn sequence( head: &str, delim: &str, tail: &str, seq_bnd: Option, seq: impl IntoIterator, ) -> Self { let items = seq.into_iter().collect_vec(); Variants::default().sequence(items.len(), head, delim, tail, seq_bnd).units_own(items) } } impl From for FmtUnit where Variants: From { fn from(value: T) -> Self { Self { subs: vec![], variants: Rc::new(Variants::from(value)) } } } impl FromStr for FmtUnit { type Err = Infallible; fn from_str(s: &str) -> Result { Ok(Self { subs: vec![], variants: Rc::new(Variants::default().bounded(s)) }) } } #[derive(Clone, Debug, Hash, PartialEq, Eq)] pub enum FmtElement { Sub { slot: u32, bounded: Option }, String(Rc), Indent(Vec), } impl FmtElement { pub fn str(s: &'_ str) -> Self { Self::String(Rc::new(s.to_string())) } pub fn sub(slot: u32, bounded: Option) -> Self { Self::Sub { slot, bounded } } pub fn bounded(i: u32) -> Self { Self::sub(i, Some(true)) } pub fn unbounded(i: u32) -> Self { Self::sub(i, Some(false)) } pub fn last(i: u32) -> Self { Self::sub(i, None) } pub fn sequence(len: usize, bounded: Option) -> Vec { match len.try_into().unwrap() { 0u32 => vec![], 1u32 => vec![FmtElement::sub(0, bounded)], n => (0..n - 1).map(FmtElement::unbounded).chain([FmtElement::sub(n - 1, bounded)]).collect(), } } pub fn from_api(api: &api::FormattingElement) -> Self { match_mapping!(api, api::FormattingElement => FmtElement { Indent(v => v.iter().map(FmtElement::from_api).collect()), String(s => Rc::new(s.clone())), Sub{ *slot, *bounded }, }) } pub fn to_api(&self) -> api::FormattingElement { match_mapping!(self, FmtElement => api::FormattingElement { Indent(v => v.iter().map(FmtElement::to_api).collect()), String(s => s.to_string()), Sub{ *slot, *bounded }, }) } } #[derive(Clone, Debug, Hash, PartialEq, Eq)] pub struct Variant { pub bounded: bool, pub elements: Vec, } #[test] fn variants_parse_test() { let vars = Rc::new(Variants::default().bounded("({{{0}}})")); let expected_vars = Rc::new(Variants(vec![Variant { bounded: true, elements: vec![ FmtElement::String(Rc::new("({".to_string())), FmtElement::Sub { bounded: Some(false), slot: 0 }, FmtElement::String(Rc::new("})".to_string())), ], }])); assert_eq!(vars.as_ref(), expected_vars.as_ref()); let unit = vars.units(["1".into()]); assert_eq!(unit, FmtUnit { subs: vec![FmtUnit { subs: vec![], variants: Rc::new(Variants(vec![Variant { bounded: true, elements: vec![FmtElement::String(Rc::new("1".to_string()))] }])) }], variants: expected_vars }); let str = take_first(&unit, true); assert_eq!(str, "({1})"); } /// Represents a collection of formatting strings for the same set of parameters /// from which the formatter can choose within their associated constraints. /// /// - {0b} can be replaced by any variant of the parameter. /// - {0} can only be replaced by a bounded variant of the parameter /// - {0l} causes the current end restriction to be applied to the parameter. /// This is to be used if the parameter is at the very end of the variant. #[derive(Clone, Debug, Hash, PartialEq, Eq, Default)] pub struct Variants(pub Vec); impl Variants { fn parse_phs(s: &'_ str) -> Vec { let re = Regex::new(r"(?\{\d+?[bl]?\})|(\{\{)|(\}\})").unwrap(); let matches = re.captures_iter(s); let slots = matches.into_iter().filter_map(|m| m.name("tpl")).map(|tpl| { let no_opencurly = tpl.as_str().strip_prefix("{").expect("required by regex"); let maybe_dash = no_opencurly.strip_suffix("}").expect("required by regex"); // we know it's not empty let last_char = maybe_dash.as_bytes()[maybe_dash.len() - 1] as char; let (num, bounded) = if !last_char.is_ascii_digit() { let bounded = match last_char { 'b' => Some(true), 'l' => None, _ => panic!("Invalid modifier char"), }; (&maybe_dash[0..maybe_dash.len() - 1], bounded) } else { (maybe_dash, Some(false)) }; let idx = num.parse::().expect("Decimal digits required by regex"); (tpl.range(), idx, bounded) }); (iter::once(None).chain(slots.into_iter().map(Some)).chain([None]).tuple_windows()) .flat_map(|(l, r)| { let string = match (l, &r) { (None, Some((r, ..))) => &s[..r.start], (Some((r1, ..)), Some((r2, ..))) => &s[r1.end..r2.start], (Some((r, ..)), None) => &s[r.end..], (None, None) => s, }; let str_item = FmtElement::String(Rc::new(string.replace("{{", "{").replace("}}", "}"))); match r { None => itertools::Either::Left([str_item]), Some((_, idx, bounded)) => itertools::Either::Right([str_item, FmtElement::Sub { slot: idx, bounded }]), } .into_iter() }) .coalesce(|left, right| match (left, right) { (FmtElement::String(left), FmtElement::String(right)) => Ok(FmtElement::String(Rc::new(left.to_string() + right.as_str()))), tuple => Err(tuple), }) .collect_vec() } fn parse(s: &'_ str) -> Vec { let mut lines = s.lines(); let Some(mut cur) = lines.next() else { return vec![] }; return indent_blk(&mut cur, &mut lines, 0); fn indent_blk<'a>( cur: &mut &'a str, lines: &mut impl Iterator, blk_lv: usize, ) -> Vec { let mut out = Vec::new(); loop { let line_lv = cur.chars().take_while(|c| *c == '\t').count(); match line_lv.cmp(&blk_lv) { Ordering::Greater => out.push(FmtElement::Indent(indent_blk(cur, lines, blk_lv + 1))), Ordering::Equal => out.extend(Variants::parse_phs(&cur[blk_lv..])), Ordering::Less => return out, } match lines.next() { Some(line) => *cur = line, None => return out, } } } } fn add(&mut self, bounded: bool, s: &'_ str) { self.0.push(Variant { bounded, elements: Self::parse(s) }) } /// This option is available in all positions. /// See [Variants] for a description of the format strings pub fn bounded(mut self, s: &'_ str) -> Self { self.add(true, s); self } /// This option is only available in positions immediately preceding the end /// of the sequence or a parenthesized subsequence. /// See [Variants] for a description of the format strings pub fn unbounded(mut self, s: &'_ str) -> Self { self.add(false, s); self } pub fn sequence( mut self, len: usize, head: &str, delim: &str, tail: &str, seq_bnd: Option, ) -> Self { let seq = chain!( [FmtElement::str(head)], Itertools::intersperse( FmtElement::sequence(len, seq_bnd).into_iter(), FmtElement::str(delim), ), [FmtElement::str(tail)], ); self.0.push(Variant { bounded: true, elements: seq.collect_vec() }); self } pub fn units_own(self, subs: impl IntoIterator) -> FmtUnit { FmtUnit::new(Rc::new(self), subs) } pub fn units(self: &Rc, subs: impl IntoIterator) -> FmtUnit { FmtUnit::new(self.clone(), subs) } } impl From> for Variants { fn from(value: Rc) -> Self { Self(vec![Variant { elements: vec![FmtElement::String(value)], bounded: true }]) } } impl From for Variants { fn from(value: String) -> Self { Self::from(Rc::new(value)) } } impl From<&str> for Variants { fn from(value: &str) -> Self { Self::from(value.to_string()) } } impl FromStr for Variants { type Err = Infallible; fn from_str(s: &str) -> Result { Ok(Self::default().bounded(s)) } } fn indent_str(s: &str, indent: u16) -> String { s.replace("\n", &format!("\n{}", "\t".repeat(indent.into()))) } fn fill_slots<'a, 'b>( elements: impl IntoIterator, values: &[FmtUnit], indent: u16, last_bounded: bool, ) -> String { elements .into_iter() .map(|el| match el { FmtElement::String(s) => indent_str(s, indent), FmtElement::Sub { slot, bounded } => indent_str(&take_first(&values[*slot as usize], bounded.unwrap_or(last_bounded)), indent), FmtElement::Indent(elements) => fill_slots(elements, values, indent + 1, last_bounded), }) .collect() } /// The simplest possible print strategy pub fn take_first(unit: &FmtUnit, bounded: bool) -> String { let first = unit.variants.0.iter().find(|v| v.bounded || bounded).expect("No bounded variant!"); fill_slots(&first.elements, &unit.subs, 0, bounded) } pub async fn take_first_fmt(v: &(impl Format + ?Sized), i: &Interner) -> String { take_first(&v.print(&FmtCtxImpl { i }).await, false) } pub struct FmtCtxImpl<'a> { pub i: &'a Interner, } pub trait FmtCtx { fn i(&self) -> &Interner; // fn print_as(&self, p: &(impl Format + ?Sized)) -> impl Future where Self: Sized { // async { // // for now, always take the first option which is probably the one-line // form let variants = p.print(self).await; // take_first(&variants, true) // } // } } impl FmtCtx for FmtCtxImpl<'_> { fn i(&self) -> &Interner { self.i } } pub trait Format { #[must_use] fn print<'a>(&'a self, c: &'a (impl FmtCtx + ?Sized + 'a)) -> impl Future + 'a; } impl Format for Never { async fn print<'a>(&'a self, _c: &'a (impl FmtCtx + ?Sized + 'a)) -> FmtUnit { match *self {} } } /// Format with default strategy. Currently equal to [take_first_fmt] pub async fn fmt(v: &(impl Format + ?Sized), i: &Interner) -> String { take_first_fmt(v, i).await } /// Format a sequence with default strategy. Currently equal to [take_first_fmt] pub async fn fmt_v>( v: impl IntoIterator, i: &Interner, ) -> impl Iterator { join_all(v.into_iter().map(|f| async move { take_first_fmt(f.borrow(), i).await })) .await .into_iter() }