1use std::{2 any::Any,3 fmt::{self, Debug, Display},4 hash::{Hash, Hasher},5 path::{Path, PathBuf},6 rc::Rc,7};89use jrsonnet_gcmodule::{Trace, Tracer};10use jrsonnet_interner::IStr;11#[cfg(feature = "serde")]12use serde::{Deserialize, Serialize};13#[cfg(feature = "structdump")]14use structdump::Codegen;1516use crate::location::{location_to_offset, offset_to_location, CodeLocation};1718macro_rules! any_ext_methods {19 ($T:ident) => {20 fn as_any(&self) -> &dyn Any;21 fn dyn_hash(&self, hasher: &mut dyn Hasher);22 fn dyn_eq(&self, other: &dyn $T) -> bool;23 fn dyn_debug(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result;24 };25}26macro_rules! any_ext_impl {27 ($T:ident) => {28 fn as_any(&self) -> &dyn Any {29 self30 }31 fn dyn_hash(&self, mut hasher: &mut dyn Hasher) {32 self.hash(&mut hasher)33 }34 fn dyn_eq(&self, other: &dyn $T) -> bool {35 let other = if let Some(v) = other.as_any().downcast_ref::<Self>() {36 v37 } else {38 return false;39 };40 let this = <Self as $T>::as_any(self)41 .downcast_ref::<Self>()42 .expect("restricted by impl");43 this == other44 }45 fn dyn_debug(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {46 <Self as std::fmt::Debug>::fmt(self, fmt)47 }48 };49}50macro_rules! any_ext {51 ($T:ident) => {52 impl Hash for dyn $T {53 fn hash<H: Hasher>(&self, state: &mut H) {54 self.dyn_hash(state)55 }56 }57 impl PartialEq for dyn $T {58 fn eq(&self, other: &Self) -> bool {59 self.dyn_eq(other)60 }61 }62 impl Eq for dyn $T {}63 };64}65pub trait SourcePathT: Trace + Debug + Display {66 67 68 fn is_default(&self) -> bool;69 fn path(&self) -> Option<&Path>;70 any_ext_methods!(SourcePathT);71}72any_ext!(SourcePathT);737475767778798081828384858687#[derive(Eq, Debug, Clone)]88pub struct SourcePath(Rc<dyn SourcePathT>);89impl SourcePath {90 pub fn new(inner: impl SourcePathT) -> Self {91 Self(Rc::new(inner))92 }93 pub fn downcast_ref<T: SourcePathT>(&self) -> Option<&T> {94 self.0.as_any().downcast_ref()95 }96 pub fn is_default(&self) -> bool {97 self.0.is_default()98 }99 pub fn path(&self) -> Option<&Path> {100 self.0.path()101 }102}103impl Hash for SourcePath {104 fn hash<H: Hasher>(&self, state: &mut H) {105 self.0.hash(state);106 }107}108impl PartialEq for SourcePath {109 #[allow(clippy::op_ref)]110 fn eq(&self, other: &Self) -> bool {111 &*self.0 == &*other.0112 }113}114impl Trace for SourcePath {115 fn trace(&self, tracer: &mut Tracer) {116 (*self.0).trace(tracer)117 }118119 fn is_type_tracked() -> bool120 where121 Self: Sized,122 {123 true124 }125}126impl Display for SourcePath {127 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {128 write!(f, "{}", self.0)129 }130}131impl Default for SourcePath {132 fn default() -> Self {133 Self(Rc::new(SourceDefault))134 }135}136137#[cfg(feature = "structdump")]138impl Codegen for SourcePath {139 fn gen_code(140 &self,141 res: &mut structdump::CodegenResult,142 unique: bool,143 ) -> structdump::TokenStream {144 let source_virtual = self145 .0146 .as_any()147 .downcast_ref::<SourceVirtual>()148 .expect("can only codegen for virtual source paths!")149 .0150 .clone();151 let val = res.add_value(source_virtual, false);152 res.add_code(153 structdump::quote! {154 structdump_import::SourcePath::new(structdump_import::SourceVirtual(#val))155 },156 Some(structdump::quote!(SourcePath)),157 unique,158 )159 }160}161162#[derive(Trace, Hash, PartialEq, Eq, Debug)]163struct SourceDefault;164impl Display for SourceDefault {165 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {166 write!(f, "<default>")167 }168}169impl SourcePathT for SourceDefault {170 fn is_default(&self) -> bool {171 true172 }173 fn path(&self) -> Option<&Path> {174 None175 }176 any_ext_impl!(SourcePathT);177}178179180181182183184#[derive(Trace, Hash, PartialEq, Eq, Debug)]185pub struct SourceFile(PathBuf);186impl SourceFile {187 pub fn new(path: PathBuf) -> Self {188 Self(path)189 }190 pub fn path(&self) -> &Path {191 &self.0192 }193}194impl Display for SourceFile {195 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {196 write!(f, "{}", self.0.display())197 }198}199impl SourcePathT for SourceFile {200 fn is_default(&self) -> bool {201 false202 }203 fn path(&self) -> Option<&Path> {204 Some(&self.0)205 }206 any_ext_impl!(SourcePathT);207}208209210211212#[derive(Trace, Hash, PartialEq, Eq, Debug)]213pub struct SourceDirectory(PathBuf);214impl SourceDirectory {215 pub fn new(path: PathBuf) -> Self {216 Self(path)217 }218 pub fn path(&self) -> &Path {219 &self.0220 }221}222impl Display for SourceDirectory {223 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {224 write!(f, "{}", self.0.display())225 }226}227impl SourcePathT for SourceDirectory {228 fn is_default(&self) -> bool {229 false230 }231 fn path(&self) -> Option<&Path> {232 Some(&self.0)233 }234 any_ext_impl!(SourcePathT);235}236237238239240241#[cfg_attr(feature = "structdump", derive(Codegen))]242#[derive(Trace, Hash, PartialEq, Eq, Debug, Clone)]243pub struct SourceVirtual(pub IStr);244impl Display for SourceVirtual {245 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {246 write!(f, "{}", self.0)247 }248}249impl SourcePathT for SourceVirtual {250 fn is_default(&self) -> bool {251 true252 }253 fn path(&self) -> Option<&Path> {254 None255 }256 any_ext_impl!(SourcePathT);257}258259260261#[cfg_attr(feature = "structdump", derive(Codegen))]262#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]263#[derive(Clone, PartialEq, Eq, Debug)]264pub struct Source(pub Rc<(SourcePath, IStr)>);265static_assertions::assert_eq_size!(Source, *const ());266267impl Trace for Source {268 fn trace(&self, _tracer: &mut Tracer) {}269270 fn is_type_tracked() -> bool {271 false272 }273}274275impl Source {276 pub fn new(path: SourcePath, code: IStr) -> Self {277 Self(Rc::new((path, code)))278 }279280 pub fn new_virtual(name: IStr, code: IStr) -> Self {281 Self::new(SourcePath::new(SourceVirtual(name)), code)282 }283284 pub fn code(&self) -> &str {285 &self.0 .1286 }287288 pub fn source_path(&self) -> &SourcePath {289 &self.0 .0290 }291292 pub fn map_source_locations(&self, locs: &[u32]) -> Vec<CodeLocation> {293 offset_to_location(&self.0 .1, locs)294 }295 pub fn map_from_source_location(&self, line: usize, column: usize) -> Option<usize> {296 location_to_offset(&self.0 .1, line, column)297 }298}