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

difftreelog

style move `#[allow(async_fn_in_trait)]` comment to the top

Petr Portnov2023-10-29parent: #101b513.patch.diff
in: master

1 file changed

modifiedcrates/jrsonnet-evaluator/src/async_import.rsdiffbeforeafterboth
before · crates/jrsonnet-evaluator/src/async_import.rs
1use std::{cell::RefCell, path::Path};23use jrsonnet_gcmodule::Trace;4use jrsonnet_interner::IStr;5use jrsonnet_parser::{6	ArgsDesc, AssertStmt, BindSpec, CompSpec, Destruct, Expr, FieldMember, FieldName, ForSpecData,7	IfSpecData, LocExpr, Member, ObjBody, Param, ParamsDesc, ParserSettings, SliceDesc, Source,8	SourcePath,9};1011use crate::{bail, gc::GcHashMap, FileData, ImportResolver, State};1213pub struct Import {14	path: IStr,15	expression: bool,16}1718pub struct FoundImports(Vec<Import>);1920// Visits all nodes, trying to find import statements21#[allow(clippy::too_many_lines)]22pub fn find_imports(expr: &LocExpr, out: &mut FoundImports) {23	fn in_destruct(dest: &Destruct, #[allow(unused_variables)] out: &mut FoundImports) {24		match dest {25			#[cfg(feature = "exp-destruct")]26			Destruct::Array {27				start,28				rest: _,29				end,30			} => {31				for dest in start {32					in_destruct(dest, out);33				}34				for dest in end {35					in_destruct(dest, out);36				}37			}38			#[cfg(feature = "exp-destruct")]39			Destruct::Object { fields, rest: _ } => {40				for (_, dest, default) in fields {41					if let Some(dest) = dest {42						in_destruct(dest, out);43					}44					if let Some(expr) = default {45						find_imports(expr, out);46					}47				}48			}49			#[cfg(feature = "exp-destruct")]50			Destruct::Skip => {}51			Destruct::Full(_) => {}52		}53	}54	fn in_compspec(specs: &[CompSpec], out: &mut FoundImports) {55		for spec in specs {56			match spec {57				CompSpec::IfSpec(IfSpecData(expr)) => find_imports(expr, out),58				CompSpec::ForSpec(ForSpecData(destruct, expr)) => {59					in_destruct(destruct, out);60					find_imports(expr, out);61				}62			}63		}64	}65	fn in_params(params: &ParamsDesc, out: &mut FoundImports) {66		for Param(dest, default) in &*params.0 {67			in_destruct(dest, out);68			if let Some(expr) = default {69				find_imports(expr, out);70			}71		}72	}73	fn in_bind(specs: &[BindSpec], out: &mut FoundImports) {74		for spec in specs {75			match spec {76				BindSpec::Field {77					into: dest,78					value: expr,79				} => {80					in_destruct(dest, out);81					find_imports(expr, out);82				}83				BindSpec::Function {84					name: _,85					params,86					value: expr,87				} => {88					in_params(params, out);89					find_imports(expr, out);90				}91			}92		}93	}94	fn in_args(ArgsDesc { unnamed, named }: &ArgsDesc, out: &mut FoundImports) {95		for expr in unnamed {96			find_imports(expr, out);97		}98		for (_, expr) in named {99			find_imports(expr, out);100		}101	}102	fn in_obj(obj: &ObjBody, out: &mut FoundImports) {103		match obj {104			ObjBody::MemberList(v) => {105				for member in v {106					match member {107						Member::Field(FieldMember {108							name,109							params,110							value,111							..112						}) => {113							match name {114								FieldName::Fixed(_) => {}115								FieldName::Dyn(expr) => find_imports(expr, out),116							}117							if let Some(params) = params {118								in_params(params, out);119							}120							find_imports(value, out);121						}122						Member::BindStmt(_) => todo!(),123						Member::AssertStmt(AssertStmt(expr, expr2)) => {124							find_imports(expr, out);125							if let Some(expr) = expr2 {126								find_imports(expr, out);127							}128						}129					}130				}131			}132			ObjBody::ObjComp(_) => todo!(),133		}134	}135	match &*expr.0 {136		Expr::Import(v) | Expr::ImportStr(v) | Expr::ImportBin(v) => {137			if let Expr::Str(s) = &*v.0 {138				out.0.push(Import {139					path: s.clone(),140					expression: matches!(&*expr.0, Expr::Import(_)),141				});142			}143			// Non-string import will fail in runtime144		}145146		Expr::Literal(_) | Expr::Str(_) | Expr::Num(_) | Expr::Var(_) => {}147148		Expr::Arr(arr) => {149			for expr in arr {150				find_imports(expr, out);151			}152		}153		Expr::ArrComp(expr, specs) => {154			find_imports(expr, out);155			in_compspec(specs, out);156		}157		Expr::Obj(obj) => in_obj(obj, out),158		Expr::ObjExtend(expr, obj) => {159			find_imports(expr, out);160			in_obj(obj, out);161		}162		Expr::BinaryOp(a, _, b) => {163			find_imports(a, out);164			find_imports(b, out);165		}166		Expr::AssertExpr(AssertStmt(expr, expr2), then) => {167			find_imports(expr, out);168			if let Some(expr) = expr2 {169				find_imports(expr, out);170			}171			find_imports(then, out);172		}173		Expr::LocalExpr(specs, expr) => {174			in_bind(specs, out);175			find_imports(expr, out);176		}177		Expr::Apply(expr, args, _) => {178			find_imports(expr, out);179			in_args(args, out);180		}181		Expr::Index { indexable, parts } => {182			find_imports(indexable, out);183			for part in parts {184				find_imports(&part.value, out);185			}186		}187		Expr::Function(params, expr) => {188			in_params(params, out);189			find_imports(expr, out);190		}191		Expr::IfElse {192			cond: IfSpecData(expr),193			cond_then,194			cond_else,195		} => {196			find_imports(expr, out);197			find_imports(cond_then, out);198			if let Some(expr) = cond_else {199				find_imports(expr, out);200			}201		}202		Expr::Slice(expr, SliceDesc { start, end, step }) => {203			find_imports(expr, out);204			if let Some(expr) = start {205				find_imports(expr, out);206			}207			if let Some(expr) = end {208				find_imports(expr, out);209			}210			if let Some(expr) = step {211				find_imports(expr, out);212			}213		}214		Expr::Parened(expr) | Expr::UnaryOp(_, expr) | Expr::ErrorStmt(expr) => {215			find_imports(expr, out);216		}217	}218}219220#[allow(async_fn_in_trait)] // we don't care about `Send` bound221pub trait AsyncImportResolver {222	type Error;223	/// Resolves file path, e.g. `(/home/user/manifests, b.libjsonnet)` can correspond224	/// both to `/home/user/manifests/b.libjsonnet` and to `/home/user/${vendor}/b.libjsonnet`225	/// where `${vendor}` is a library path.226	///227	/// `from` should only be returned from [`ImportResolver::resolve`], or from other defined file, any other value228	/// may result in panic229	async fn resolve_from(&self, from: &SourcePath, path: &str) -> Result<SourcePath, Self::Error>;230	async fn resolve_from_default(&self, path: &str) -> Result<SourcePath, Self::Error> {231		self.resolve_from(&SourcePath::default(), path).await232	}233	/// Resolves absolute path, doesn't supports jpath and other fancy things234	async fn resolve(&self, path: &Path) -> Result<SourcePath, Self::Error>;235236	/// Load resolved file237	/// This should only be called with value returned from [`ImportResolver::resolve_file`]/[`ImportResolver::resolve`],238	/// this cannot be resolved using associated type, as evaluator uses object instead of generic for [`ImportResolver`]239	async fn load_file_contents(&self, resolved: &SourcePath) -> Result<Vec<u8>, Self::Error>;240}241242#[derive(Trace)]243struct ResolvedImportResolver {244	resolved: RefCell<GcHashMap<(SourcePath, IStr), (SourcePath, bool)>>,245}246impl ImportResolver for ResolvedImportResolver {247	fn load_file_contents(&self, _resolved: &SourcePath) -> crate::Result<Vec<u8>> {248		unreachable!("all files should be loaded at this point");249	}250251	fn resolve_from(&self, from: &SourcePath, path: &str) -> crate::Result<SourcePath> {252		Ok(self253			.resolved254			.borrow()255			.get(&(from.clone(), path.into()))256			.expect("all imports should be resolved at this point")257			.0258			.clone())259	}260261	fn resolve_from_default(&self, path: &str) -> crate::Result<SourcePath> {262		self.resolve_from(&SourcePath::default(), path)263	}264265	fn resolve(&self, path: &Path) -> crate::Result<SourcePath> {266		bail!(crate::error::ErrorKind::AbsoluteImportNotSupported(267			path.to_owned()268		))269	}270271	fn as_any(&self) -> &dyn std::any::Any {272		self273	}274}275276enum Job {277	LoadFile { path: SourcePath, parse: bool },278	ParseFile(SourcePath),279	ResolveImport { from: SourcePath, import: Import },280}281282#[allow(clippy::future_not_send)]283pub async fn async_import<H>(s: State, handler: H, path: impl AsRef<Path>) -> Result<(), H::Error>284where285	H: AsyncImportResolver,286{287	let mut resolved = s288		.import_resolver()289		.as_any()290		.downcast_ref::<ResolvedImportResolver>()291		.map_or_else(GcHashMap::new, |resolver| {292			std::mem::take(&mut *resolver.resolved.borrow_mut())293		});294	let mut queue = vec![Job::LoadFile {295		path: handler.resolve(path.as_ref()).await?,296		parse: true,297	}];298	while let Some(job) = queue.pop() {299		match job {300			Job::LoadFile { path, parse } => {301				if !s.0.file_cache.borrow().contains_key(&path) {302					let data = handler.load_file_contents(&path).await?;303					s.0.file_cache304						.borrow_mut()305						.insert(path.clone(), FileData::new_bytes(data.as_slice().into()));306				}307				if parse {308					queue.push(Job::ParseFile(path));309				}310			}311			Job::ParseFile(path) => {312				if let Some(file) = s.0.file_cache.borrow_mut().get_mut(&path) {313					if file.parsed.is_none() {314						let Some(code) = file.get_string() else {315							continue;316						};317						let source = Source::new(path.clone(), code.clone());318						// If failed - then skip import319						file.parsed =320							jrsonnet_parser::parse(&code, &ParserSettings { source }).ok();321						if let Some(parsed) = &file.parsed {322							let mut imports = FoundImports(vec![]);323							find_imports(parsed, &mut imports);324							for import in imports.0 {325								queue.push(Job::ResolveImport {326									from: path.clone(),327									import,328								});329							}330						}331					}332				}333			}334			Job::ResolveImport { from, import } => {335				if let Some((resolved, expression)) =336					resolved.get_mut(&(from.clone(), import.path.clone()))337				{338					if import.expression && !*expression {339						*expression = true;340						queue.push(Job::ParseFile(resolved.clone()));341					}342					continue;343				}344				let resolved = handler.resolve_from(&from, &import.path).await?;345				queue.push(Job::LoadFile {346					path: resolved,347					parse: import.expression,348				});349			}350		}351	}352	s.set_import_resolver(ResolvedImportResolver {353		resolved: RefCell::new(resolved),354	});355	Ok(())356}
after · crates/jrsonnet-evaluator/src/async_import.rs
1use std::{cell::RefCell, path::Path};23use jrsonnet_gcmodule::Trace;4use jrsonnet_interner::IStr;5use jrsonnet_parser::{6	ArgsDesc, AssertStmt, BindSpec, CompSpec, Destruct, Expr, FieldMember, FieldName, ForSpecData,7	IfSpecData, LocExpr, Member, ObjBody, Param, ParamsDesc, ParserSettings, SliceDesc, Source,8	SourcePath,9};1011use crate::{bail, gc::GcHashMap, FileData, ImportResolver, State};1213pub struct Import {14	path: IStr,15	expression: bool,16}1718pub struct FoundImports(Vec<Import>);1920// Visits all nodes, trying to find import statements21#[allow(clippy::too_many_lines)]22pub fn find_imports(expr: &LocExpr, out: &mut FoundImports) {23	fn in_destruct(dest: &Destruct, #[allow(unused_variables)] out: &mut FoundImports) {24		match dest {25			#[cfg(feature = "exp-destruct")]26			Destruct::Array {27				start,28				rest: _,29				end,30			} => {31				for dest in start {32					in_destruct(dest, out);33				}34				for dest in end {35					in_destruct(dest, out);36				}37			}38			#[cfg(feature = "exp-destruct")]39			Destruct::Object { fields, rest: _ } => {40				for (_, dest, default) in fields {41					if let Some(dest) = dest {42						in_destruct(dest, out);43					}44					if let Some(expr) = default {45						find_imports(expr, out);46					}47				}48			}49			#[cfg(feature = "exp-destruct")]50			Destruct::Skip => {}51			Destruct::Full(_) => {}52		}53	}54	fn in_compspec(specs: &[CompSpec], out: &mut FoundImports) {55		for spec in specs {56			match spec {57				CompSpec::IfSpec(IfSpecData(expr)) => find_imports(expr, out),58				CompSpec::ForSpec(ForSpecData(destruct, expr)) => {59					in_destruct(destruct, out);60					find_imports(expr, out);61				}62			}63		}64	}65	fn in_params(params: &ParamsDesc, out: &mut FoundImports) {66		for Param(dest, default) in &*params.0 {67			in_destruct(dest, out);68			if let Some(expr) = default {69				find_imports(expr, out);70			}71		}72	}73	fn in_bind(specs: &[BindSpec], out: &mut FoundImports) {74		for spec in specs {75			match spec {76				BindSpec::Field {77					into: dest,78					value: expr,79				} => {80					in_destruct(dest, out);81					find_imports(expr, out);82				}83				BindSpec::Function {84					name: _,85					params,86					value: expr,87				} => {88					in_params(params, out);89					find_imports(expr, out);90				}91			}92		}93	}94	fn in_args(ArgsDesc { unnamed, named }: &ArgsDesc, out: &mut FoundImports) {95		for expr in unnamed {96			find_imports(expr, out);97		}98		for (_, expr) in named {99			find_imports(expr, out);100		}101	}102	fn in_obj(obj: &ObjBody, out: &mut FoundImports) {103		match obj {104			ObjBody::MemberList(v) => {105				for member in v {106					match member {107						Member::Field(FieldMember {108							name,109							params,110							value,111							..112						}) => {113							match name {114								FieldName::Fixed(_) => {}115								FieldName::Dyn(expr) => find_imports(expr, out),116							}117							if let Some(params) = params {118								in_params(params, out);119							}120							find_imports(value, out);121						}122						Member::BindStmt(_) => todo!(),123						Member::AssertStmt(AssertStmt(expr, expr2)) => {124							find_imports(expr, out);125							if let Some(expr) = expr2 {126								find_imports(expr, out);127							}128						}129					}130				}131			}132			ObjBody::ObjComp(_) => todo!(),133		}134	}135	match &*expr.0 {136		Expr::Import(v) | Expr::ImportStr(v) | Expr::ImportBin(v) => {137			if let Expr::Str(s) = &*v.0 {138				out.0.push(Import {139					path: s.clone(),140					expression: matches!(&*expr.0, Expr::Import(_)),141				});142			}143			// Non-string import will fail in runtime144		}145146		Expr::Literal(_) | Expr::Str(_) | Expr::Num(_) | Expr::Var(_) => {}147148		Expr::Arr(arr) => {149			for expr in arr {150				find_imports(expr, out);151			}152		}153		Expr::ArrComp(expr, specs) => {154			find_imports(expr, out);155			in_compspec(specs, out);156		}157		Expr::Obj(obj) => in_obj(obj, out),158		Expr::ObjExtend(expr, obj) => {159			find_imports(expr, out);160			in_obj(obj, out);161		}162		Expr::BinaryOp(a, _, b) => {163			find_imports(a, out);164			find_imports(b, out);165		}166		Expr::AssertExpr(AssertStmt(expr, expr2), then) => {167			find_imports(expr, out);168			if let Some(expr) = expr2 {169				find_imports(expr, out);170			}171			find_imports(then, out);172		}173		Expr::LocalExpr(specs, expr) => {174			in_bind(specs, out);175			find_imports(expr, out);176		}177		Expr::Apply(expr, args, _) => {178			find_imports(expr, out);179			in_args(args, out);180		}181		Expr::Index { indexable, parts } => {182			find_imports(indexable, out);183			for part in parts {184				find_imports(&part.value, out);185			}186		}187		Expr::Function(params, expr) => {188			in_params(params, out);189			find_imports(expr, out);190		}191		Expr::IfElse {192			cond: IfSpecData(expr),193			cond_then,194			cond_else,195		} => {196			find_imports(expr, out);197			find_imports(cond_then, out);198			if let Some(expr) = cond_else {199				find_imports(expr, out);200			}201		}202		Expr::Slice(expr, SliceDesc { start, end, step }) => {203			find_imports(expr, out);204			if let Some(expr) = start {205				find_imports(expr, out);206			}207			if let Some(expr) = end {208				find_imports(expr, out);209			}210			if let Some(expr) = step {211				find_imports(expr, out);212			}213		}214		Expr::Parened(expr) | Expr::UnaryOp(_, expr) | Expr::ErrorStmt(expr) => {215			find_imports(expr, out);216		}217	}218}219220// we don't care about `Send` bound221#[allow(async_fn_in_trait)]222pub trait AsyncImportResolver {223	type Error;224	/// Resolves file path, e.g. `(/home/user/manifests, b.libjsonnet)` can correspond225	/// both to `/home/user/manifests/b.libjsonnet` and to `/home/user/${vendor}/b.libjsonnet`226	/// where `${vendor}` is a library path.227	///228	/// `from` should only be returned from [`ImportResolver::resolve`], or from other defined file, any other value229	/// may result in panic230	async fn resolve_from(&self, from: &SourcePath, path: &str) -> Result<SourcePath, Self::Error>;231	async fn resolve_from_default(&self, path: &str) -> Result<SourcePath, Self::Error> {232		self.resolve_from(&SourcePath::default(), path).await233	}234	/// Resolves absolute path, doesn't supports jpath and other fancy things235	async fn resolve(&self, path: &Path) -> Result<SourcePath, Self::Error>;236237	/// Load resolved file238	/// This should only be called with value returned from [`ImportResolver::resolve_file`]/[`ImportResolver::resolve`],239	/// this cannot be resolved using associated type, as evaluator uses object instead of generic for [`ImportResolver`]240	async fn load_file_contents(&self, resolved: &SourcePath) -> Result<Vec<u8>, Self::Error>;241}242243#[derive(Trace)]244struct ResolvedImportResolver {245	resolved: RefCell<GcHashMap<(SourcePath, IStr), (SourcePath, bool)>>,246}247impl ImportResolver for ResolvedImportResolver {248	fn load_file_contents(&self, _resolved: &SourcePath) -> crate::Result<Vec<u8>> {249		unreachable!("all files should be loaded at this point");250	}251252	fn resolve_from(&self, from: &SourcePath, path: &str) -> crate::Result<SourcePath> {253		Ok(self254			.resolved255			.borrow()256			.get(&(from.clone(), path.into()))257			.expect("all imports should be resolved at this point")258			.0259			.clone())260	}261262	fn resolve_from_default(&self, path: &str) -> crate::Result<SourcePath> {263		self.resolve_from(&SourcePath::default(), path)264	}265266	fn resolve(&self, path: &Path) -> crate::Result<SourcePath> {267		bail!(crate::error::ErrorKind::AbsoluteImportNotSupported(268			path.to_owned()269		))270	}271272	fn as_any(&self) -> &dyn std::any::Any {273		self274	}275}276277enum Job {278	LoadFile { path: SourcePath, parse: bool },279	ParseFile(SourcePath),280	ResolveImport { from: SourcePath, import: Import },281}282283#[allow(clippy::future_not_send)]284pub async fn async_import<H>(s: State, handler: H, path: impl AsRef<Path>) -> Result<(), H::Error>285where286	H: AsyncImportResolver,287{288	let mut resolved = s289		.import_resolver()290		.as_any()291		.downcast_ref::<ResolvedImportResolver>()292		.map_or_else(GcHashMap::new, |resolver| {293			std::mem::take(&mut *resolver.resolved.borrow_mut())294		});295	let mut queue = vec![Job::LoadFile {296		path: handler.resolve(path.as_ref()).await?,297		parse: true,298	}];299	while let Some(job) = queue.pop() {300		match job {301			Job::LoadFile { path, parse } => {302				if !s.0.file_cache.borrow().contains_key(&path) {303					let data = handler.load_file_contents(&path).await?;304					s.0.file_cache305						.borrow_mut()306						.insert(path.clone(), FileData::new_bytes(data.as_slice().into()));307				}308				if parse {309					queue.push(Job::ParseFile(path));310				}311			}312			Job::ParseFile(path) => {313				if let Some(file) = s.0.file_cache.borrow_mut().get_mut(&path) {314					if file.parsed.is_none() {315						let Some(code) = file.get_string() else {316							continue;317						};318						let source = Source::new(path.clone(), code.clone());319						// If failed - then skip import320						file.parsed =321							jrsonnet_parser::parse(&code, &ParserSettings { source }).ok();322						if let Some(parsed) = &file.parsed {323							let mut imports = FoundImports(vec![]);324							find_imports(parsed, &mut imports);325							for import in imports.0 {326								queue.push(Job::ResolveImport {327									from: path.clone(),328									import,329								});330							}331						}332					}333				}334			}335			Job::ResolveImport { from, import } => {336				if let Some((resolved, expression)) =337					resolved.get_mut(&(from.clone(), import.path.clone()))338				{339					if import.expression && !*expression {340						*expression = true;341						queue.push(Job::ParseFile(resolved.clone()));342					}343					continue;344				}345				let resolved = handler.resolve_from(&from, &import.path).await?;346				queue.push(Job::LoadFile {347					path: resolved,348					parse: import.expression,349				});350			}351		}352	}353	s.set_import_resolver(ResolvedImportResolver {354		resolved: RefCell::new(resolved),355	});356	Ok(())357}