git.delta.rocks / jrsonnet / refs/commits / a7e60a9a0ce5

difftreelog

feat custom SourcePath trait

Yaroslav Bolyukin2022-08-27parent: #3c7fc76.patch.diff
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

modifiedcrates/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,
modifiedcrates/jrsonnet-parser/src/source.rsdiffbeforeafterboth
1use 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};
78
1415
15use crate::location::{location_to_offset, offset_to_location, CodeLocation};16use crate::location::{location_to_offset, offset_to_location, CodeLocation};
1617
17#[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 self
30 }
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 v
37 } 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 == other
44 }
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 input
67 /// if `true` - then resolver may threat this path as default, and default is usally a CWD
68 fn is_default(&self) -> bool;
69 fn path(&self) -> Option<&Path>;
70 any_ext_methods!(SourcePathT);
71}
72any_ext!(SourcePathT);
73
74/// Represents location of a file
75///
76/// Standard CLI only operates using
77/// - [`SourceFile`] - for any file
78/// - [`SourceDirectory`] - for resolution from CWD
79/// - [`SourceVirtual`] - for stdlib/ext-str
80///
81/// From all of those, only [`SourceVirtual`] may be constructed manually, any other path kind should be only obtained
82/// from assigned `ImportResolver`
83/// However, you should always check `is_default` method return, as it will return true for any paths, where default
84/// search location is applicable
85///
86/// Resolver may also return custom implementations of this trait, for example it may return http url in case of remotely loaded files
19#[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.0
112 }
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 }
30118
31 fn is_type_tracked() -> bool {119 fn is_type_tracked() -> bool
32 false120 where
121 Self: Sized,
122 {
123 true
33 }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}
136
137#[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 = self
145 .0
146 .as_any()
147 .downcast_ref::<SourceVirtual>()
148 .expect("can only codegen for virtual source paths!")
149 .0
150 .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}
161
162#[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 true
172 }
173 fn path(&self) -> Option<&Path> {
174 None
175 }
176 any_ext_impl!(SourcePathT);
177}
178
179/// Represents path to the file on the disk
180/// 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.0
192 }
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 false
202 }
203 fn path(&self) -> Option<&Path> {
204 Some(&self.0)
205 }
206 any_ext_impl!(SourcePathT);
207}
208
209/// Represents path to the directory on the disk
210///
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.0
220 }
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 false
230 }
231 fn path(&self) -> Option<&Path> {
232 Some(&self.0)
233 }
234 any_ext_impl!(SourcePathT);
235}
236
237/// Represents virtual file, whose are located in memory, and shouldn't be cached
238///
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 resolver
241#[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 true
252 }
253 fn path(&self) -> Option<&Path> {
254 None
255 }
256 any_ext_impl!(SourcePathT);
257}
42258
43/// Either real file, or virtual259/// Either real file, or virtual
44/// 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_mut
57}273}
58274
59impl Source {275impl Source {
60 /// Fails when path contains inner /../ or /./ references, or not absolute
61 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 || path
65 .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 }
73279
74 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 }
77283
78 pub fn short_display(&self) -> ShortDisplay {
79 ShortDisplay(self.clone())
80 }
81
82 /// Returns Some if this file is loaded from FS
83 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 .1
92 }286 }
93287
94 pub fn source_path(&self) -> &SourcePath {288 pub fn source_path(&self) -> &SourcePath {
95 &self.0 .0 as &SourcePath289 &self.0 .0
96 }290 }
97291
98 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