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.rsdiffbeforeafterboth--- a/crates/jrsonnet-parser/src/lib.rs
+++ b/crates/jrsonnet-parser/src/lib.rs
@@ -11,7 +11,7 @@
mod source;
mod unescape;
pub use location::CodeLocation;
-pub use source::{Source, SourcePath};
+pub use source::{Source, SourceDirectory, SourceFile, SourcePath, SourcePathT, SourceVirtual};
pub struct ParserSettings {
pub file_name: Source,
crates/jrsonnet-parser/src/source.rsdiffbeforeafterboth1use std::{2 borrow::Cow,3 fmt,4 path::{Component, Path, PathBuf},5 rc::Rc,6};78use jrsonnet_gcmodule::{Trace, Tracer};9use jrsonnet_interner::IStr;10#[cfg(feature = "serde")]11use serde::{Deserialize, Serialize};12#[cfg(feature = "structdump")]13use structdump::Codegen;1415use crate::location::{location_to_offset, offset_to_location, CodeLocation};1617#[cfg_attr(feature = "structdump", derive(Codegen))]18#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]19#[derive(PartialEq, Eq, Debug, Hash, Clone)]20pub enum SourcePath {21 /// This file is located on disk22 Path(PathBuf),23 /// This file is located somewhere else (I.e http), but it can refer to relative paths, and is egilible for caching24 Custom(String),25 /// This file is only located in memory, and can't be cached26 Virtual(Cow<'static, str>),27}28impl Trace for SourcePath {29 fn trace(&self, _tracer: &mut Tracer) {}3031 fn is_type_tracked() -> bool {32 false33 }34}3536impl SourcePath {37 /// Should import resolver be able to read file by this path?38 pub fn can_load(&self) -> bool {39 matches!(self, Self::Path(_) | Self::Custom(_))40 }41}4243/// Either real file, or virtual44/// Hash of FileName always have same value as raw Path, to make it possible to use with raw_entry_mut45#[cfg_attr(feature = "structdump", derive(Codegen))]46#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]47#[derive(Clone, PartialEq, Eq, Debug)]48pub struct Source(pub Rc<(SourcePath, IStr)>);49static_assertions::assert_eq_size!(Source, *const ());5051impl Trace for Source {52 fn trace(&self, _tracer: &mut Tracer) {}5354 fn is_type_tracked() -> bool {55 false56 }57}5859impl Source {60 /// Fails when path contains inner /../ or /./ references, or not absolute61 pub fn new(path: SourcePath, code: IStr) -> Option<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))))72 }7374 pub fn new_virtual(n: Cow<'static, str>, code: IStr) -> Self {75 Self(Rc::new((SourcePath::Virtual(n), code)))76 }7778 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 {91 &self.0 .192 }9394 pub fn source_path(&self) -> &SourcePath {95 &self.0 .0 as &SourcePath96 }9798 pub fn map_source_locations(&self, locs: &[u32]) -> Vec<CodeLocation> {99 offset_to_location(&self.0 .1, locs)100 }101 pub fn map_from_source_location(&self, line: usize, column: usize) -> Option<usize> {102 location_to_offset(&self.0 .1, line, column)103 }104}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}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 /// 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 files87#[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}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}258259/// Either real file, or virtual260/// Hash of FileName always have same value as raw Path, to make it possible to use with raw_entry_mut261#[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}