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::Acyclic;10use jrsonnet_interner::{IBytes, IStr};11use url::Url;1213use crate::location::{CodeLocation, location_to_offset, offset_to_location};1415macro_rules! any_ext_methods {16 ($T:ident) => {17 fn as_any(&self) -> &dyn Any;18 fn dyn_hash(&self, hasher: &mut dyn Hasher);19 fn dyn_eq(&self, other: &dyn $T) -> bool;20 fn dyn_debug(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result;21 };22}23macro_rules! any_ext_impl {24 ($T:ident) => {25 fn as_any(&self) -> &dyn Any {26 self27 }28 fn dyn_hash(&self, mut hasher: &mut dyn Hasher) {29 self.hash(&mut hasher)30 }31 fn dyn_eq(&self, other: &dyn $T) -> bool {32 let Some(other) = other.as_any().downcast_ref::<Self>() else {33 return false;34 };35 let this = <Self as $T>::as_any(self)36 .downcast_ref::<Self>()37 .expect("restricted by impl");38 this == other39 }40 fn dyn_debug(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {41 <Self as std::fmt::Debug>::fmt(self, fmt)42 }43 };44}45macro_rules! any_ext {46 ($T:ident) => {47 impl Hash for dyn $T {48 fn hash<H: Hasher>(&self, state: &mut H) {49 self.dyn_hash(state)50 }51 }52 impl PartialEq for dyn $T {53 fn eq(&self, other: &Self) -> bool {54 self.dyn_eq(other)55 }56 }57 impl Eq for dyn $T {}58 };59}60pub trait SourcePathT: Acyclic + Debug + Display {61 62 63 fn is_default(&self) -> bool;64 fn path(&self) -> Option<&Path>;65 any_ext_methods!(SourcePathT);66}67any_ext!(SourcePathT);68697071727374757677787980818283#[derive(Eq, Clone, Acyclic)]84pub struct SourcePath(Rc<dyn SourcePathT>);85impl SourcePath {86 pub fn new(inner: impl SourcePathT) -> Self {87 Self(Rc::new(inner))88 }89 pub fn downcast_ref<T: SourcePathT>(&self) -> Option<&T> {90 self.0.as_any().downcast_ref()91 }92 pub fn is_default(&self) -> bool {93 self.0.is_default()94 }95 pub fn path(&self) -> Option<&Path> {96 self.0.path()97 }98}99impl Hash for SourcePath {100 fn hash<H: Hasher>(&self, state: &mut H) {101 self.0.hash(state);102 }103}104impl PartialEq for SourcePath {105 #[allow(clippy::op_ref)]106 fn eq(&self, other: &Self) -> bool {107 &*self.0 == &*other.0108 }109}110impl Display for SourcePath {111 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {112 write!(f, "{}", self.0)113 }114}115impl Debug for SourcePath {116 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {117 write!(f, "{:?}", self.0)118 }119}120impl Default for SourcePath {121 fn default() -> Self {122 Self(Rc::new(SourceDefault))123 }124}125126#[derive(Acyclic, Hash, PartialEq, Eq, Debug)]127struct SourceDefault;128impl Display for SourceDefault {129 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {130 write!(f, "<default>")131 }132}133impl SourcePathT for SourceDefault {134 fn is_default(&self) -> bool {135 true136 }137 fn path(&self) -> Option<&Path> {138 None139 }140 any_ext_impl!(SourcePathT);141}142143#[derive(Acyclic, Hash, PartialEq, Eq, Debug)]144pub struct SourceDefaultIgnoreJpath;145impl Display for SourceDefaultIgnoreJpath {146 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {147 write!(f, "<default (ignoring jpath)>")148 }149}150impl SourcePathT for SourceDefaultIgnoreJpath {151 fn is_default(&self) -> bool {152 true153 }154 fn path(&self) -> Option<&Path> {155 None156 }157 any_ext_impl!(SourcePathT);158}159160161162163164165#[derive(Acyclic, Hash, PartialEq, Eq, Debug)]166pub struct SourceFile(PathBuf);167impl SourceFile {168 pub fn new(path: PathBuf) -> Self {169 Self(path)170 }171 pub fn path(&self) -> &Path {172 &self.0173 }174}175impl Display for SourceFile {176 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {177 write!(f, "{}", self.0.display())178 }179}180impl SourcePathT for SourceFile {181 fn is_default(&self) -> bool {182 false183 }184 fn path(&self) -> Option<&Path> {185 Some(&self.0)186 }187 any_ext_impl!(SourcePathT);188}189190#[derive(Acyclic, Hash, PartialEq, Eq, Debug)]191pub struct SourceUrl(Url);192impl SourceUrl {193 pub fn new(url: Url) -> Self {194 Self(url)195 }196}197impl Display for SourceUrl {198 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {199 write!(f, "{}", self.0)200 }201}202impl SourcePathT for SourceUrl {203 fn is_default(&self) -> bool {204 false205 }206 fn path(&self) -> Option<&Path> {207 None208 }209 any_ext_impl!(SourcePathT);210}211212213214215#[derive(Acyclic, Hash, PartialEq, Eq, Debug)]216pub struct SourceDirectory(PathBuf);217impl SourceDirectory {218 pub fn new(path: PathBuf) -> Self {219 Self(path)220 }221 pub fn path(&self) -> &Path {222 &self.0223 }224}225impl Display for SourceDirectory {226 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {227 write!(f, "{}", self.0.display())228 }229}230impl SourcePathT for SourceDirectory {231 fn is_default(&self) -> bool {232 false233 }234 fn path(&self) -> Option<&Path> {235 Some(&self.0)236 }237 any_ext_impl!(SourcePathT);238}239240241242243244#[derive(Acyclic, Hash, PartialEq, Eq, Clone)]245pub struct SourceVirtual(pub IStr);246impl Display for SourceVirtual {247 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {248 write!(f, "virtual:{}", self.0)249 }250}251impl fmt::Debug for SourceVirtual {252 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {253 write!(f, "virtual:{}", self.0)254 }255}256impl SourcePathT for SourceVirtual {257 fn is_default(&self) -> bool {258 true259 }260 fn path(&self) -> Option<&Path> {261 None262 }263 any_ext_impl!(SourcePathT);264}265266267268269270271#[allow(clippy::derived_hash_with_manual_eq)]272#[derive(Acyclic, Debug, Hash)]273pub struct SourceFifo(pub String, pub IBytes);274impl PartialEq for SourceFifo {275 fn eq(&self, other: &Self) -> bool {276 std::ptr::eq(self, other)277 }278}279impl fmt::Display for SourceFifo {280 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {281 write!(f, "fifo({:?})", self.0)282 }283}284impl SourcePathT for SourceFifo {285 fn is_default(&self) -> bool {286 287 true288 }289290 fn path(&self) -> Option<&Path> {291 None292 }293294 any_ext_impl!(SourcePathT);295}296297298299#[derive(Clone, PartialEq, Eq, Acyclic)]300pub struct Source(pub Rc<(SourcePath, IStr)>);301302impl Source {303 pub fn new(path: SourcePath, code: IStr) -> Self {304 Self(Rc::new((path, code)))305 }306307 pub fn new_virtual(name: IStr, code: IStr) -> Self {308 Self::new(SourcePath::new(SourceVirtual(name)), code)309 }310311 pub fn code(&self) -> &str {312 &self.0.1313 }314315 pub fn source_path(&self) -> &SourcePath {316 &self.0.0317 }318319 pub fn map_source_locations<const S: usize>(&self, locs: &[u32; S]) -> [CodeLocation; S] {320 offset_to_location(&self.0.1, locs)321 }322 pub fn map_from_source_location(&self, line: usize, column: usize) -> Option<usize> {323 location_to_offset(&self.0.1, line, column)324 }325}326impl fmt::Debug for Source {327 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {328 write!(f, "{:?}", self.0.0)329 }330}