difftreelog
feat custom SourcePath trait
in: master
For usage in new import resolvers, this change allows to use custom types within importers, allowing to allow imports from internet
2 files changed
crates/jrsonnet-parser/src/lib.rsdiffbeforeafterboth11mod source;11mod source;12mod unescape;12mod unescape;13pub use location::CodeLocation;13pub use location::CodeLocation;14pub use source::{Source, SourcePath};14pub use source::{Source, SourceDirectory, SourceFile, SourcePath, SourcePathT, SourceVirtual};151516pub struct ParserSettings {16pub struct ParserSettings {17 pub file_name: Source,17 pub file_name: Source,crates/jrsonnet-parser/src/source.rsdiffbeforeafterboth1use std::{1use std::{2 borrow::Cow,2 any::Any,3 fmt,3 fmt::{self, Debug, Display},4 hash::{Hash, Hasher},4 path::{Component, Path, PathBuf},5 path::{Path, PathBuf},5 rc::Rc,6 rc::Rc,6};7};78141515use crate::location::{location_to_offset, offset_to_location, CodeLocation};16use crate::location::{location_to_offset, offset_to_location, CodeLocation};161717#[cfg_attr(feature = "structdump", derive(Codegen))]18macro_rules! any_ext_methods {18#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]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 /// This method should be checked by resolver before panicking with bad SourcePath input67 /// if `true` - then resolver may threat this path as default, and default is usally a CWD68 fn is_default(&self) -> bool;69 fn path(&self) -> Option<&Path>;70 any_ext_methods!(SourcePathT);71}72any_ext!(SourcePathT);7374/// Represents location of a file75///76/// Standard CLI only operates using77/// - [`SourceFile`] - for any file78/// - [`SourceDirectory`] - for resolution from CWD79/// - [`SourceVirtual`] - for stdlib/ext-str80///81/// From all of those, only [`SourceVirtual`] may be constructed manually, any other path kind should be only obtained82/// from assigned `ImportResolver`83/// However, you should always check `is_default` method return, as it will return true for any paths, where default84/// search location is applicable85///86/// Resolver may also return custom implementations of this trait, for example it may return http url in case of remotely loaded files19#[derive(PartialEq, Eq, Debug, Hash, Clone)]87#[derive(Eq, Debug, Clone)]20pub enum SourcePath {88pub struct SourcePath(Rc<dyn SourcePathT>);21 /// This file is located on disk89impl SourcePath {22 Path(PathBuf),90 pub fn new(inner: impl SourcePathT) -> Self {23 /// This file is located somewhere else (I.e http), but it can refer to relative paths, and is egilible for caching91 Self(Rc::new(inner))24 Custom(String),92 }25 /// This file is only located in memory, and can't be cached93 pub fn downcast_ref<T: SourcePathT>(&self) -> Option<&T> {26 Virtual(Cow<'static, str>),94 self.0.as_any().downcast_ref()27}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}28impl Trace for SourcePath {114impl Trace for SourcePath {29 fn trace(&self, _tracer: &mut Tracer) {}115 fn trace(&self, tracer: &mut Tracer) {116 (*self.0).trace(tracer)117 }3011831 fn is_type_tracked() -> bool {119 fn is_type_tracked() -> bool32 false120 where121 Self: Sized,122 {123 true33 }124 }34}125}35126impl 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")]36impl SourcePath {138impl Codegen for SourcePath {37 /// Should import resolver be able to read file by this path?38 pub fn can_load(&self) -> bool {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(39 matches!(self, Self::Path(_) | Self::Custom(_))153 structdump::quote! {154 structdump_import::SourcePath::new(structdump_import::SourceVirtual(#val))155 },156 Some(structdump::quote!(SourcePath)),157 unique,158 )40 }159 }41}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}178179/// Represents path to the file on the disk180/// Directories shouldn't be put here, as resolution for files differs from resolution for directories:181///182/// When `file` is being resolved from `SourceFile(a/b/c)`, it should be resolved to `SourceFile(a/b/file)`,183/// however if it is being resolved from `SourceDirectory(a/b/c)`, then it should be resolved to `SourceDirectory(a/b/c/file)`184#[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}208209/// Represents path to the directory on the disk210///211/// See also [`SourceFile`]212#[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}236237/// Represents virtual file, whose are located in memory, and shouldn't be cached238///239/// It is used for --ext-code=.../--tla-code=.../standard library source code by default,240/// and user can construct arbitrary values by hand, without asking import resolver241#[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}4225843/// Either real file, or virtual259/// Either real file, or virtual44/// Hash of FileName always have same value as raw Path, to make it possible to use with raw_entry_mut260/// Hash of FileName always have same value as raw Path, to make it possible to use with raw_entry_mut57}273}5827459impl Source {275impl Source {60 /// Fails when path contains inner /../ or /./ references, or not absolute61 pub fn new(path: SourcePath, code: IStr) -> Option<Self> {276 pub fn new(path: SourcePath, code: IStr) -> Self {62 if let SourcePath::Path(path) = &path {63 if !path.is_absolute()64 || path65 .components()66 .any(|c| matches!(c, Component::CurDir | Component::ParentDir))67 {68 return None;69 }70 }71 Some(Self(Rc::new((path, code))))277 Self(Rc::new((path, code)))72 }278 }7327974 pub fn new_virtual(n: Cow<'static, str>, code: IStr) -> Self {280 pub fn new_virtual(name: IStr, code: IStr) -> Self {75 Self(Rc::new((SourcePath::Virtual(n), code)))281 Self::new(SourcePath::new(SourceVirtual(name)), code)76 }282 }7728378 pub fn short_display(&self) -> ShortDisplay {79 ShortDisplay(self.clone())80 }8182 /// Returns Some if this file is loaded from FS83 pub fn path(&self) -> Option<&Path> {84 match self.source_path() {85 SourcePath::Path(r) => Some(r),86 SourcePath::Custom(_) => None,87 SourcePath::Virtual(_) => None,88 }89 }90 pub fn code(&self) -> &str {284 pub fn code(&self) -> &str {91 &self.0 .1285 &self.0 .192 }286 }9328794 pub fn source_path(&self) -> &SourcePath {288 pub fn source_path(&self) -> &SourcePath {95 &self.0 .0 as &SourcePath289 &self.0 .096 }290 }9729198 pub fn map_source_locations(&self, locs: &[u32]) -> Vec<CodeLocation> {292 pub fn map_source_locations(&self, locs: &[u32]) -> Vec<CodeLocation> {102 location_to_offset(&self.0 .1, line, column)296 location_to_offset(&self.0 .1, line, column)103 }297 }104}298}105pub struct ShortDisplay(Source);106impl fmt::Display for ShortDisplay {107 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {108 match &self.0 .0 .0 as &SourcePath {109 SourcePath::Path(r) => {110 write!(111 f,112 "{}",113 r.file_name().expect("path is valid").to_string_lossy()114 )115 }116 SourcePath::Custom(r) => write!(f, "{}", r),117 SourcePath::Virtual(n) => write!(f, "{}", n),118 }119 }120}121299