use std::{
cmp,
cmp::Ordering,
fmt,
fmt::{Display, Formatter},
ops::Deref,
sync::Arc,
};
use serde::{Deserialize, Serialize};
#[repr(transparent)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
pub struct StatementColumn(pub u32);
impl Deref for StatementColumn {
type Target = u32;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl PartialEq<i32> for StatementColumn {
fn eq(&self, other: &i32) -> bool {
self.0 == *other as u32
}
}
#[repr(transparent)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
pub struct StatementLine(pub u32);
impl Deref for StatementLine {
type Target = u32;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl PartialEq<i32> for StatementLine {
fn eq(&self, other: &i32) -> bool {
self.0 == *other as u32
}
}
#[derive(Debug, Clone, PartialEq, Hash, Serialize, Deserialize, Default)]
pub enum Fragment {
#[default]
None,
Statement {
text: Arc<str>,
line: StatementLine,
column: StatementColumn,
},
Internal {
text: Arc<str>,
},
}
impl Fragment {
pub fn text(&self) -> &str {
match self {
Fragment::None => "",
Fragment::Statement {
text,
..
}
| Fragment::Internal {
text,
..
} => text,
}
}
pub fn line(&self) -> StatementLine {
match self {
Fragment::Statement {
line,
..
} => *line,
_ => StatementLine(1),
}
}
pub fn column(&self) -> StatementColumn {
match self {
Fragment::Statement {
column,
..
} => *column,
_ => StatementColumn(0),
}
}
pub fn sub_fragment(&self, offset: usize, length: usize) -> Fragment {
let text = self.text();
let end = cmp::min(offset + length, text.len());
let sub_text = if offset < text.len() {
&text[offset..end]
} else {
""
};
match self {
Fragment::None => Fragment::None,
Fragment::Statement {
line,
column,
..
} => Fragment::Statement {
text: Arc::from(sub_text),
line: *line,
column: StatementColumn(column.0 + offset as u32),
},
Fragment::Internal {
..
} => Fragment::Internal {
text: Arc::from(sub_text),
},
}
}
pub fn with_text(&self, text: impl Into<String>) -> Fragment {
let text = Arc::from(text.into());
match self {
Fragment::Statement {
line,
column,
..
} => Fragment::Statement {
text,
line: *line,
column: *column,
},
Fragment::Internal {
..
} => Fragment::Internal {
text,
},
Fragment::None => Fragment::Internal {
text,
},
}
}
}
impl Fragment {
pub fn internal(text: impl Into<String>) -> Self {
Fragment::Internal {
text: Arc::from(text.into()),
}
}
pub fn testing(text: impl Into<String>) -> Self {
Fragment::Statement {
text: Arc::from(text.into()),
line: StatementLine(1),
column: StatementColumn(0),
}
}
pub fn testing_empty() -> Self {
Self::testing("")
}
pub fn merge_all(fragments: impl IntoIterator<Item = Fragment>) -> Fragment {
let mut fragments: Vec<Fragment> = fragments.into_iter().collect();
assert!(!fragments.is_empty());
fragments.sort();
let first = fragments.first().unwrap();
let mut text = String::with_capacity(fragments.iter().map(|f| f.text().len()).sum());
for fragment in &fragments {
text.push_str(fragment.text());
}
match first {
Fragment::None => Fragment::None,
Fragment::Statement {
line,
column,
..
} => Fragment::Statement {
text: Arc::from(text),
line: *line,
column: *column,
},
Fragment::Internal {
..
} => Fragment::Internal {
text: Arc::from(text),
},
}
}
pub fn fragment(&self) -> &str {
self.text()
}
}
impl AsRef<str> for Fragment {
fn as_ref(&self) -> &str {
self.text()
}
}
impl Display for Fragment {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
Display::fmt(self.text(), f)
}
}
impl PartialOrd for Fragment {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Fragment {
fn cmp(&self, other: &Self) -> Ordering {
self.column().cmp(&other.column()).then(self.line().cmp(&other.line()))
}
}
impl Eq for Fragment {}
impl From<String> for Fragment {
fn from(s: String) -> Self {
Fragment::Internal {
text: Arc::from(s),
}
}
}
impl From<&str> for Fragment {
fn from(s: &str) -> Self {
Fragment::Internal {
text: Arc::from(s),
}
}
}
impl Fragment {
pub fn statement(text: impl Into<String>, line: u32, column: u32) -> Self {
Fragment::Statement {
text: Arc::from(text.into()),
line: StatementLine(line),
column: StatementColumn(column),
}
}
pub fn none() -> Self {
Fragment::None
}
}
impl PartialEq<str> for Fragment {
fn eq(&self, other: &str) -> bool {
self.text() == other
}
}
impl PartialEq<&str> for Fragment {
fn eq(&self, other: &&str) -> bool {
self.text() == *other
}
}
impl PartialEq<String> for Fragment {
fn eq(&self, other: &String) -> bool {
self.text() == other.as_str()
}
}
impl PartialEq<String> for &Fragment {
fn eq(&self, other: &String) -> bool {
self.text() == other.as_str()
}
}
pub trait LazyFragment {
fn fragment(&self) -> Fragment;
}
impl<F> LazyFragment for F
where
F: Fn() -> Fragment,
{
fn fragment(&self) -> Fragment {
self()
}
}
impl LazyFragment for &Fragment {
fn fragment(&self) -> Fragment {
(*self).clone()
}
}
impl LazyFragment for Fragment {
fn fragment(&self) -> Fragment {
self.clone()
}
}