use std::str::FromStr;
use miette::{MietteError, MietteSpanContents, SourceCode, SourceSpan, SpanContents};
mod code_display;
mod diag;
mod identifier;
mod ord_map;
mod output;
mod rc;
mod source;
mod src_ref;
mod tree_display;
pub use compact_str::{CompactString, ToCompactString};
pub type Id = CompactString;
pub use url::Url;
pub fn virtual_url(name: &str) -> Url {
Url::from_str(&format!("virtual://{name}")).unwrap()
}
pub trait ResourceLocation {
fn url(&self) -> &Url;
fn to_file_path(&self) -> Option<std::path::PathBuf> {
if self.url().scheme() == "file" {
self.url().to_file_path().ok()
} else {
None
}
}
fn is_local(&self) -> bool {
self.url().scheme() == "file"
}
fn relative_path(&self) -> Option<std::path::PathBuf> {
self.to_file_path().map(|path| {
let current_dir = std::env::current_dir().expect("current dir");
if let Ok(path) = path.canonicalize() {
pathdiff::diff_paths(path, current_dir).unwrap_or_default()
} else {
path.to_path_buf()
}
})
}
fn source_name(&self) -> String {
self.relative_path()
.map(|s| s.to_string_lossy().to_string())
.unwrap_or(self.url().path().to_string())
}
}
pub const MICROCAD_EXTENSIONS: &[&str] = &["µcad", "mcad", "ucad"];
pub use code_display::*;
pub use diag::{
Diag, DiagError, DiagHandler, DiagRenderOptions, DiagResult, Diagnostic, Diagnostics, Level,
PushDiag,
};
pub use identifier::Identifier;
pub use ord_map::{OrdMap, OrdMapValue};
pub use output::{Capture, Output, Stdout};
pub use rc::{Rc, RcMut};
pub use src_ref::{LineCol, LineIndex, Refer, Span, SrcRef, SrcReferrer};
pub use tree_display::{FormatTree, TreeDisplay, TreeState};
pub use microcad_core::hash::{ComputedHash, HashId, HashMap, HashSet, Hashed};
pub use source::Source;
pub struct SourceLocInfo<'a> {
pub code: &'a str,
pub url: Url,
pub line_offset: u32,
}
impl SourceLocInfo<'static> {
pub fn invalid() -> Self {
SourceLocInfo {
code: "NO FILE",
url: virtual_url("invalid"),
line_offset: 0,
}
}
}
impl<'a> ResourceLocation for SourceLocInfo<'a> {
fn url(&self) -> &Url {
&self.url
}
}
impl SourceCode for SourceLocInfo<'_> {
fn read_span<'a>(
&'a self,
span: &SourceSpan,
context_lines_before: usize,
context_lines_after: usize,
) -> Result<Box<dyn SpanContents<'a> + 'a>, MietteError> {
let inner_contents =
self.code
.read_span(span, context_lines_before, context_lines_after)?;
let contents = MietteSpanContents::new_named(
self.source_name(),
inner_contents.data(),
*inner_contents.span(),
inner_contents.line() + self.line_offset as usize,
inner_contents.column(),
inner_contents.line_count(),
)
.with_language("µcad");
Ok(Box::new(contents))
}
}
pub trait GetSourceLocInfoByHash {
fn get_source_loc_info_by_hash(&'_ self, hash: HashId) -> Option<SourceLocInfo<'_>>;
}
pub fn shorten(what: &str, max_chars: usize) -> String {
let short: String = what
.chars()
.enumerate()
.filter_map(|(p, ch)| {
if p == max_chars {
Some('…')
} else if p < max_chars {
if ch == '\n' { Some('⏎') } else { Some(ch) }
} else {
None
}
})
.collect();
if cfg!(feature = "ansi-color") && short.contains('\x1b') {
short + "\x1b[0m"
} else {
short
}
}
#[cfg(feature = "ansi-color")]
#[macro_export]
macro_rules! mark {
(FOUND!) => {
color_print::cformat!("<G!,k,s> FOUND </>")
};
(FOUND) => {
color_print::cformat!("<W!,k,s> FOUND </>")
};
(MATCH) => {
color_print::cformat!("<Y!,k,s> MATCH </>")
};
(NO_MATCH) => {
color_print::cformat!("<Y,k,s> NO MATCH </>")
};
(MATCH!) => {
color_print::cformat!("<G!,k,s> MATCH </>")
};
(NO_MATCH!) => {
color_print::cformat!("<R,k,s> NO MATCH </>")
};
(CALL) => {
color_print::cformat!("<B,k,s> CALL </>")
};
(LOOKUP) => {
color_print::cformat!("<c,s>LOOKUP</>")
};
(LOAD) => {
color_print::cformat!("<Y,k,s> LOADING </>")
};
(RESOLVE) => {
color_print::cformat!("<M,k,s> RESOLVE </>")
};
(AMBIGUOUS) => {
color_print::cformat!("<R,k,s> AMBIGUOUS </>")
};
(NOT_FOUND!) => {
color_print::cformat!("<R,k,s> NOT FOUND </>")
};
(NOT_FOUND) => {
color_print::cformat!("<Y,k,s> NOT FOUND </>")
};
}
pub trait WriteToFile: std::fmt::Display {
fn write_to_file(&self, filename: &impl AsRef<std::path::Path>) -> std::io::Result<()> {
use std::io::Write;
let file = std::fs::File::create(filename)?;
let mut writer = std::io::BufWriter::new(file);
write!(writer, "{self}")
}
}