use core::ops::{Deref, DerefMut};
use crate::{
ast::Expr,
convert::{FromRon, ToRon},
error::{Result, Span},
};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Spanned<T> {
pub value: T,
pub span: Span,
}
impl<T> Spanned<T> {
pub fn new(value: T, span: Span) -> Self {
Self { value, span }
}
pub fn get(&self) -> &T {
&self.value
}
pub fn get_mut(&mut self) -> &mut T {
&mut self.value
}
pub fn into_inner(self) -> T {
self.value
}
pub fn map<U, F>(self, f: F) -> Spanned<U>
where
F: FnOnce(T) -> U,
{
Spanned {
value: f(self.value),
span: self.span,
}
}
pub fn try_map<U, E, F>(self, f: F) -> Result<Spanned<U>, E>
where
F: FnOnce(T) -> Result<U, E>,
{
Ok(Spanned {
value: f(self.value)?,
span: self.span,
})
}
pub fn span(&self) -> &Span {
&self.span
}
}
impl<T> Deref for Spanned<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.value
}
}
impl<T> DerefMut for Spanned<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.value
}
}
impl<T: FromRon> FromRon for Spanned<T> {
fn from_ast(expr: &Expr<'_>) -> Result<Self> {
Ok(Spanned {
value: T::from_ast(expr)?,
span: *expr.span(),
})
}
}
impl<T: ToRon> ToRon for Spanned<T> {
fn to_ast(&self) -> Result<Expr<'static>> {
self.value.to_ast()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::error::Position;
fn make_span(line: usize, col_val: usize) -> Span {
Span {
start: Position { line, col: col_val },
end: Position {
line,
col: col_val + 1,
},
start_offset: 0,
end_offset: 1,
}
}
#[test]
fn test_new_and_accessors() {
let span = make_span(1, 1);
let spanned = Spanned::new(42, span);
assert_eq!(spanned.value, 42);
assert_eq!(*spanned.get(), 42);
assert_eq!(*spanned, 42); assert_eq!(*spanned.span(), span);
}
#[test]
fn test_get_mut() {
let span = make_span(1, 1);
let mut spanned = Spanned::new(42, span);
*spanned.get_mut() = 100;
assert_eq!(spanned.value, 100);
*spanned = 200; assert_eq!(spanned.value, 200);
}
#[test]
fn test_into_inner() {
let span = make_span(1, 1);
let spanned = Spanned::new(42, span);
let value = spanned.into_inner();
assert_eq!(value, 42);
}
#[test]
fn test_map() {
let span = make_span(1, 1);
let spanned = Spanned::new(42, span);
let doubled = spanned.map(|x| x * 2);
assert_eq!(doubled.value, 84);
assert_eq!(doubled.span, span);
}
#[test]
fn test_try_map_success() {
let span = make_span(1, 1);
let spanned = Spanned::new("42", span);
let parsed: core::result::Result<Spanned<i32>, _> = spanned.try_map(str::parse);
let parsed = parsed.unwrap();
assert_eq!(parsed.value, 42);
assert_eq!(parsed.span, span);
}
#[test]
fn test_try_map_failure() {
let span = make_span(1, 1);
let spanned = Spanned::new("not a number", span);
let parsed: core::result::Result<Spanned<i32>, _> = spanned.try_map(str::parse);
assert!(parsed.is_err());
}
#[test]
fn test_from_ron_primitive() {
let ron = "42";
let spanned: Spanned<i32> = Spanned::from_ron(ron).unwrap();
assert_eq!(spanned.value, 42);
assert_eq!(spanned.span.start.line, 1);
assert_eq!(spanned.span.start.col, 1);
}
#[test]
fn test_from_ron_string() {
let ron = r#""hello""#;
let spanned: Spanned<String> = Spanned::from_ron(ron).unwrap();
assert_eq!(spanned.value, "hello");
assert_eq!(spanned.span.start.line, 1);
}
#[test]
fn test_from_ron_bool() {
let ron = "true";
let spanned: Spanned<bool> = Spanned::from_ron(ron).unwrap();
assert!(spanned.value);
assert_eq!(spanned.span.start.line, 1);
}
#[test]
fn test_to_ron_discards_span() {
let span = make_span(5, 10);
let spanned = Spanned::new(42, span);
let serialized = spanned.to_ron().unwrap();
assert_eq!(serialized, "42");
let deserialized: Spanned<i32> = Spanned::from_ron(&serialized).unwrap();
assert_eq!(deserialized.value, 42);
assert_eq!(deserialized.span.start.line, 1); }
#[test]
fn test_roundtrip_value_preserved() {
let spanned = Spanned::new("test string", make_span(1, 1));
let serialized = spanned.to_ron().unwrap();
let deserialized: Spanned<String> = Spanned::from_ron(&serialized).unwrap();
assert_eq!(deserialized.value, "test string");
}
#[test]
fn test_vec_of_spanned() {
let ron = "[1, 2, 3]";
let spanned_vec: Vec<Spanned<i32>> = Vec::from_ron(ron).unwrap();
assert_eq!(spanned_vec.len(), 3);
assert_eq!(spanned_vec[0].value, 1);
assert_eq!(spanned_vec[1].value, 2);
assert_eq!(spanned_vec[2].value, 3);
assert!(spanned_vec[0].span.start.col < spanned_vec[1].span.start.col);
assert!(spanned_vec[1].span.start.col < spanned_vec[2].span.start.col);
}
#[test]
fn test_option_spanned_some() {
let ron = "Some(42)";
let opt: Option<Spanned<i32>> = Option::from_ron(ron).unwrap();
assert!(opt.is_some());
let spanned = opt.unwrap();
assert_eq!(spanned.value, 42);
assert!(spanned.span.start.line > 0); }
#[test]
fn test_option_spanned_none() {
let ron = "None";
let opt: Option<Spanned<i32>> = Option::from_ron(ron).unwrap();
assert!(opt.is_none()); }
#[test]
fn test_spanned_option_some() {
let ron = "Some(42)";
let spanned: Spanned<Option<i32>> = Spanned::from_ron(ron).unwrap();
assert_eq!(spanned.value, Some(42));
assert!(spanned.span.start.line > 0); }
#[test]
fn test_spanned_option_none() {
let ron = "None";
let spanned: Spanned<Option<i32>> = Spanned::from_ron(ron).unwrap();
assert_eq!(spanned.value, None);
assert!(spanned.span.start.line > 0); }
#[test]
fn test_implicit_some_with_option_spanned() {
let ron = "42"; let opt: Option<Spanned<i32>> = Option::from_ron(ron).unwrap();
assert!(opt.is_some());
let spanned = opt.unwrap();
assert_eq!(spanned.value, 42);
}
#[test]
fn test_implicit_some_with_spanned_option() {
let ron = "42"; let spanned: Spanned<Option<i32>> = Spanned::from_ron(ron).unwrap();
assert_eq!(spanned.value, Some(42)); }
#[test]
fn test_multiline_span() {
let ron = r#"
"hello
world"
"#;
let spanned: Spanned<String> = Spanned::from_ron(ron.trim()).unwrap();
assert_eq!(spanned.value, "hello\nworld");
assert!(spanned.span.end.line > spanned.span.start.line);
}
#[test]
fn test_clone() {
let span = make_span(1, 1);
let spanned = Spanned::new(42, span);
let cloned = spanned.clone();
assert_eq!(cloned.value, spanned.value);
assert_eq!(cloned.span, spanned.span);
}
#[test]
fn test_eq() {
let span1 = make_span(1, 1);
let span2 = make_span(2, 2);
let spanned1 = Spanned::new(42, span1);
let spanned2 = Spanned::new(42, span1);
let spanned3 = Spanned::new(42, span2);
let spanned4 = Spanned::new(100, span1);
assert_eq!(spanned1, spanned2); assert_ne!(spanned1, spanned3); assert_ne!(spanned1, spanned4); }
}