use std::fmt::{Debug, Display, Formatter};
use std::num::NonZeroU32;
use tanzim_source::Source;
#[derive(Debug, Clone, PartialEq)]
pub struct Location {
pub source: Source,
pub line: Option<NonZeroU32>,
pub column: Option<NonZeroU32>,
pub length: Option<NonZeroU32>,
pub snippet: String,
}
fn position(value: usize) -> Option<NonZeroU32> {
NonZeroU32::new(u32::try_from(value).unwrap_or(u32::MAX))
}
impl Location {
pub fn in_source(
source: Source,
line: Option<usize>,
column: Option<usize>,
length: Option<usize>,
) -> Self {
Self {
source,
line: line.and_then(position),
column: column.and_then(position),
length: length.and_then(position),
snippet: String::new(),
}
}
pub fn in_text(
source: Source,
text: &str,
line: Option<usize>,
column: Option<usize>,
length: Option<usize>,
) -> Self {
let mut snippet = String::new();
if let Some(line_number) = line {
let highlight = length.unwrap_or(1).max(1);
let lines: Vec<&str> = text.split('\n').collect();
let offending = line_number.saturating_sub(1);
let start = offending.saturating_sub(3);
let end = (offending + 4).min(lines.len());
let gutter_width = end.to_string().len();
let mut rows: Vec<String> = Vec::new();
for (offset, line_text) in lines[start..end].iter().enumerate() {
let display_line = start + offset + 1;
let number = display_line.to_string();
let pad = gutter_width.saturating_sub(number.len());
let mut row = String::from(" ");
for _ in 0..pad {
row.push(' ');
}
row.push_str(&number);
row.push_str(" | ");
row.push_str(line_text);
rows.push(row);
if display_line == line_number {
let mut caret = String::from(" ");
for _ in 0..pad + number.len() + 1 {
caret.push(' ');
}
caret.push_str("| ");
if let Some(column_number) = column {
for _ in 1..column_number {
caret.push(' ');
}
}
for _ in 0..highlight {
caret.push('^');
}
rows.push(caret);
}
}
snippet = rows.join("\n");
}
Self {
source,
line: line.and_then(position),
column: column.and_then(position),
length: length.and_then(position),
snippet,
}
}
pub fn at(
source_name: &str,
resource: &str,
line: Option<usize>,
column: Option<usize>,
length: Option<usize>,
) -> Self {
Self::in_source(
Source::named(source_name).with_resource(resource),
line,
column,
length,
)
}
pub fn source_name(&self) -> &str {
self.source.source()
}
pub fn resource(&self) -> &str {
self.source.resource()
}
pub fn with_length(mut self, length: usize) -> Self {
self.length = position(length);
self
}
}
impl Display for Location {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let resource = self.source.resource();
if resource.is_empty() {
write!(f, "{}", self.source.source())?;
} else {
write!(f, "{}:{}", self.source.source(), resource)?;
}
match (self.line, self.column) {
(Some(line), Some(column)) => write!(f, ":{line}:{column}"),
(Some(line), None) => write!(f, ":{line}"),
_ => Ok(()),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum ValueType {
Bool,
Int,
Float,
String,
List,
Map,
Null,
}
impl Display for ValueType {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.write_str(match self {
Self::Bool => "boolean",
Self::Int => "integer",
Self::Float => "float",
Self::String => "string",
Self::List => "list",
Self::Map => "map",
Self::Null => "null",
})
}
}
#[derive(Debug, Clone, PartialEq, Default)]
pub struct Map {
entries: Vec<(String, LocatedValue)>,
}
impl Map {
pub fn new() -> Self {
Self::default()
}
pub fn len(&self) -> usize {
self.entries.len()
}
pub fn is_empty(&self) -> bool {
self.entries.is_empty()
}
pub fn contains_key(&self, key: &str) -> bool {
for index in (0..self.entries.len()).rev() {
if self.entries[index].0 == key {
return true;
}
}
false
}
pub fn get(&self, key: &str) -> Option<&LocatedValue> {
for index in (0..self.entries.len()).rev() {
if self.entries[index].0 == key {
return Some(&self.entries[index].1);
}
}
None
}
pub fn get_mut(&mut self, key: &str) -> Option<&mut LocatedValue> {
let mut found = None;
for index in (0..self.entries.len()).rev() {
if self.entries[index].0 == key {
found = Some(index);
break;
}
}
if let Some(index) = found {
Some(&mut self.entries[index].1)
} else {
None
}
}
pub fn insert(&mut self, key: String, value: LocatedValue) -> Option<LocatedValue> {
let old = self.remove(&key);
self.entries.push((key, value));
old
}
pub fn remove(&mut self, key: &str) -> Option<LocatedValue> {
let mut found = None;
for index in (0..self.entries.len()).rev() {
if self.entries[index].0 == key {
found = Some(index);
break;
}
}
if let Some(index) = found {
Some(self.entries.remove(index).1)
} else {
None
}
}
pub fn entries(&self) -> &[(String, LocatedValue)] {
&self.entries
}
pub fn entries_mut(&mut self) -> &mut Vec<(String, LocatedValue)> {
&mut self.entries
}
}
impl Display for Map {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let alternate = f.alternate();
let mut map = f.debug_map();
for (key, value) in &self.entries {
if alternate {
map.entry(key, &format_args!("{:#}", value));
} else {
map.entry(key, &format_args!("{}", value));
}
}
map.finish()
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum Value {
Bool(bool),
Int(isize),
Float(f64),
String(String),
List(Vec<LocatedValue>),
Map(Map),
Null,
}
impl From<bool> for Value {
fn from(value: bool) -> Self {
Value::Bool(value)
}
}
impl From<isize> for Value {
fn from(value: isize) -> Self {
Value::Int(value)
}
}
impl From<f64> for Value {
fn from(value: f64) -> Self {
Value::Float(value)
}
}
impl From<String> for Value {
fn from(value: String) -> Self {
Value::String(value)
}
}
impl From<&str> for Value {
fn from(value: &str) -> Self {
Value::String(value.to_string())
}
}
#[derive(Debug, Clone, PartialEq, Default)]
pub struct Comment {
before: Vec<String>,
after: Option<String>,
}
impl Comment {
pub fn new() -> Self {
Self::default()
}
pub fn before(&self) -> &[String] {
&self.before
}
pub fn before_mut(&mut self) -> &mut Vec<String> {
&mut self.before
}
pub fn after(&self) -> Option<&str> {
self.after.as_deref()
}
pub fn after_mut(&mut self) -> &mut Option<String> {
&mut self.after
}
pub fn with_before(mut self, lines: impl IntoIterator<Item = impl Into<String>>) -> Self {
self.before = lines.into_iter().map(|l| l.into()).collect();
self
}
pub fn with_after(mut self, text: Option<impl Into<String>>) -> Self {
self.after = text.map(|t| t.into());
self
}
pub fn set_before(&mut self, lines: impl IntoIterator<Item = impl Into<String>>) {
self.before = lines.into_iter().map(|l| l.into()).collect();
}
pub fn set_after(&mut self, text: Option<impl Into<String>>) {
self.after = text.map(|t| t.into());
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct LocatedValue {
value: Value,
location: Location,
comment: Comment,
}
impl LocatedValue {
pub fn new(value: impl Into<Value>, location: impl Into<Location>) -> Self {
Self {
value: value.into(),
location: location.into(),
comment: Comment::new(),
}
}
pub fn value(&self) -> &Value {
&self.value
}
pub fn value_mut(&mut self) -> &mut Value {
&mut self.value
}
pub fn into_value(self) -> Value {
self.value
}
pub fn with_value(mut self, value: impl Into<Value>) -> Self {
self.value = value.into();
self
}
pub fn set_value(&mut self, value: impl Into<Value>) {
self.value = value.into();
}
pub fn location(&self) -> &Location {
&self.location
}
pub fn location_mut(&mut self) -> &mut Location {
&mut self.location
}
pub fn with_location(mut self, location: impl Into<Location>) -> Self {
self.location = location.into();
self
}
pub fn set_location(&mut self, location: impl Into<Location>) {
self.location = location.into();
}
pub fn comment(&self) -> &Comment {
&self.comment
}
pub fn comment_mut(&mut self) -> &mut Comment {
&mut self.comment
}
pub fn with_comment(mut self, comment: Comment) -> Self {
self.comment = comment;
self
}
pub fn set_comment(&mut self, comment: Comment) {
self.comment = comment;
}
}
impl Display for LocatedValue {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
if f.alternate() {
let mut map = f.debug_map();
map.entry(&"value", &format_args!("{:#}", self.value));
map.entry(
&"location",
&format_args!("{:?}", self.location.to_string()),
);
if !self.comment.before.is_empty() || self.comment.after.is_some() {
map.entry(&"comment_before", &self.comment.before.as_slice());
if let Some(after) = &self.comment.after {
map.entry(&"comment_after", &after.as_str());
}
}
map.finish()
} else {
write!(f, "{}", self.value)
}
}
}
impl AsRef<Value> for Value {
fn as_ref(&self) -> &Value {
self
}
}
impl AsRef<Value> for LocatedValue {
fn as_ref(&self) -> &Value {
&self.value
}
}
impl Value {
pub fn new_map() -> Self {
Self::Map(Map::new())
}
pub fn new_list() -> Self {
Self::List(Vec::new())
}
pub fn new_string() -> Self {
Self::String(String::new())
}
pub fn is_bool(&self) -> bool {
matches!(self, Self::Bool(_))
}
pub fn as_bool(&self) -> Option<bool> {
match self {
Self::Bool(value) => Some(*value),
_ => None,
}
}
pub fn into_bool(self) -> Option<bool> {
match self {
Self::Bool(value) => Some(value),
_ => None,
}
}
pub fn bool_mut(&mut self) -> Option<&mut bool> {
match self {
Self::Bool(value) => Some(value),
_ => None,
}
}
pub fn is_int(&self) -> bool {
matches!(self, Self::Int(_))
}
pub fn as_int(&self) -> Option<isize> {
match self {
Self::Int(value) => Some(*value),
_ => None,
}
}
pub fn into_int(self) -> Option<isize> {
match self {
Self::Int(value) => Some(value),
_ => None,
}
}
pub fn int_mut(&mut self) -> Option<&mut isize> {
match self {
Self::Int(value) => Some(value),
_ => None,
}
}
pub fn is_float(&self) -> bool {
matches!(self, Self::Float(_))
}
pub fn as_float(&self) -> Option<f64> {
match self {
Self::Float(value) => Some(*value),
_ => None,
}
}
pub fn into_float(self) -> Option<f64> {
match self {
Self::Float(value) => Some(value),
_ => None,
}
}
pub fn float_mut(&mut self) -> Option<&mut f64> {
match self {
Self::Float(value) => Some(value),
_ => None,
}
}
pub fn is_string(&self) -> bool {
matches!(self, Self::String(_))
}
pub fn as_string(&self) -> Option<&String> {
match self {
Self::String(value) => Some(value),
_ => None,
}
}
pub fn into_string(self) -> Option<String> {
match self {
Self::String(value) => Some(value),
_ => None,
}
}
pub fn string_mut(&mut self) -> Option<&mut String> {
match self {
Self::String(value) => Some(value),
_ => None,
}
}
pub fn is_list(&self) -> bool {
matches!(self, Self::List(_))
}
pub fn as_list(&self) -> Option<&Vec<LocatedValue>> {
match self {
Self::List(value) => Some(value),
_ => None,
}
}
pub fn into_list(self) -> Option<Vec<LocatedValue>> {
match self {
Self::List(value) => Some(value),
_ => None,
}
}
pub fn list_mut(&mut self) -> Option<&mut Vec<LocatedValue>> {
match self {
Self::List(value) => Some(value),
_ => None,
}
}
pub fn is_map(&self) -> bool {
matches!(self, Self::Map(_))
}
pub fn as_map(&self) -> Option<&Map> {
match self {
Self::Map(value) => Some(value),
_ => None,
}
}
pub fn into_map(self) -> Option<Map> {
match self {
Self::Map(value) => Some(value),
_ => None,
}
}
pub fn map_mut(&mut self) -> Option<&mut Map> {
match self {
Self::Map(value) => Some(value),
_ => None,
}
}
pub fn is_null(&self) -> bool {
matches!(self, Self::Null)
}
pub fn type_name(&self) -> ValueType {
match self {
Self::Bool(_) => ValueType::Bool,
Self::Int(_) => ValueType::Int,
Self::Float(_) => ValueType::Float,
Self::String(_) => ValueType::String,
Self::List(_) => ValueType::List,
Self::Map(_) => ValueType::Map,
Self::Null => ValueType::Null,
}
}
}
impl Display for Value {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
Self::Bool(value) => write!(f, "{value}"),
Self::Int(value) => write!(f, "{value}"),
Self::Float(value) => write!(f, "{value}"),
Self::String(value) => write!(f, "{value:?}"),
Self::List(values) => {
let alternate = f.alternate();
let mut list = f.debug_list();
for value in values {
if alternate {
list.entry(&format_args!("{:#}", value));
} else {
list.entry(&format_args!("{}", value));
}
}
list.finish()
}
Self::Map(value) => Display::fmt(value, f),
Self::Null => f.write_str("null"),
}
}
}