use crate::context::RenderContext;
use crate::diagnostic::Diagnostic;
use crate::expanded::{self, Inputs};
use crate::host::{CacheKey, Data, Fetch, Files, Needed, Packs, Pending};
use crate::load;
use crate::parse::{self, Change, ParseOptions, TextEdit};
use crate::policy::Policy;
use crate::resolve::{self, Ambient, Store};
pub use crate::resolve::{HostComponent, LibraryModule, Module};
use crate::tree::Move;
use crate::{expand, tree, validate};
use std::rc::Rc;
mod evaluate;
pub struct Processor<S> {
diagnostics: Vec<Diagnostic>,
state: S,
}
pub struct New {
path: String,
text: String,
}
pub struct Parsed {
path: String,
text: String,
document: parse::Parsed,
options: ParseOptions,
}
#[allow(
clippy::struct_field_names,
reason = "`resolved` is stage 2's result, named as in the later states"
)]
pub struct Resolved {
store: HostFiles,
resolved: resolve::Resolved,
edit_context: EditContext,
}
struct HostFiles {
files: Rc<dyn Files>,
packs: Rc<dyn Packs>,
}
impl HostFiles {
fn store(&self) -> Store<'_> {
Store {
files: &*self.files,
packs: &*self.packs,
}
}
}
struct EditContext {
path: String,
options: ParseOptions,
parsed: parse::Parsed,
ambient: Ambient,
policy: Policy,
parses: resolve::Parses,
}
pub struct Validated {
store: HostFiles,
resolved: resolve::Resolved,
checks: validate::Validated,
kept: usize,
edit_context: EditContext,
allowed_origins: Vec<String>,
}
struct RenderInputs {
context: RenderContext,
fetch: Rc<dyn Fetch>,
data: Rc<dyn Data>,
}
impl RenderInputs {
fn new(context: RenderContext, fetch: impl Fetch + 'static, data: impl Data + 'static) -> Self {
Self {
context,
fetch: Rc::new(fetch),
data: Rc::new(data),
}
}
}
pub struct Loaded {
validated: Validated,
render: RenderInputs,
data: load::Loaded,
}
pub struct Expanded {
validated: Validated,
render: RenderInputs,
data: load::Loaded,
options: expand::Options,
result: expand::Expanded,
replaced: expand::Replaced,
}
#[derive(Clone, Debug, PartialEq)]
pub struct Splice {
pub from: usize,
pub to: usize,
pub nodes: Vec<expanded::Node>,
pub lines: isize,
pub offset: isize,
}
impl<S> Processor<S> {
#[must_use]
pub fn diagnostics(&self) -> &[Diagnostic] {
&self.diagnostics
}
fn then<T>(self, step: impl FnOnce(S, &mut Vec<Diagnostic>) -> T) -> Processor<T> {
let Processor {
mut diagnostics,
state,
} = self;
let state = step(state, &mut diagnostics);
Processor { diagnostics, state }
}
}
impl Processor<New> {
#[must_use]
pub fn new(path: impl Into<String>, text: impl Into<String>) -> Self {
Self {
diagnostics: Vec::new(),
state: New {
path: path.into(),
text: text.into(),
},
}
}
#[must_use]
pub fn parse(self, options: &ParseOptions) -> Processor<Parsed> {
self.then(|New { path, text }, diagnostics| {
let document = parse::parse(&text, options);
Parsed::new(path, text, document, options.clone(), diagnostics)
})
}
}
impl Parsed {
fn new(
path: String,
text: String,
document: parse::Parsed,
options: ParseOptions,
diagnostics: &mut Vec<Diagnostic>,
) -> Self {
diagnostics.extend_from_slice(&document.diagnostics);
Self {
path,
text,
document,
options,
}
}
}
impl Processor<Parsed> {
#[must_use]
pub fn tree(&self) -> &tree::Root {
&self.state.document.root
}
#[must_use]
pub fn resolve(
self,
files: impl Files + 'static,
packs: impl Packs + 'static,
ambient: &Ambient,
) -> Processor<Resolved> {
self.resolve_with(files, packs, ambient, &Policy::default())
}
#[must_use]
pub fn resolve_with(
self,
files: impl Files + 'static,
packs: impl Packs + 'static,
ambient: &Ambient,
policy: &Policy,
) -> Processor<Resolved> {
let store = HostFiles {
files: Rc::new(files),
packs: Rc::new(packs),
};
self.resolve_in(
store,
ambient.clone(),
policy.clone(),
resolve::Parses::default(),
)
}
fn resolve_in(
self,
store: HostFiles,
ambient: Ambient,
policy: Policy,
mut parses: resolve::Parses,
) -> Processor<Resolved> {
self.then(|parsed, diagnostics| {
let Parsed {
path,
text,
document,
options,
} = parsed;
let resolved = resolve::resolve(
&path,
text,
document.for_resolving(),
store.store(),
resolve::Host {
ambient: &ambient,
policy: &policy,
},
&mut parses,
);
let edit_context = EditContext {
path,
options,
parsed: document,
ambient,
policy,
parses,
};
diagnostics.extend(resolved.diagnostics().cloned());
Resolved {
store,
resolved,
edit_context,
}
})
}
}
impl Processor<Resolved> {
#[must_use]
pub fn validate(self, allowed_origins: &[String]) -> Processor<Validated> {
self.then(
|Resolved {
store,
resolved,
edit_context,
},
diagnostics| {
let checks = validate::validate(&resolved, allowed_origins);
diagnostics.extend(checks.diagnostics().cloned());
Validated {
store,
resolved,
checks,
kept: diagnostics.len(),
edit_context,
allowed_origins: allowed_origins.to_vec(),
}
},
)
}
}
impl Processor<Validated> {
#[must_use]
pub fn sources(&self, context: &RenderContext) -> Needed {
load::sources(
&self.state.resolved,
&self.state.checks,
context,
&self.state.allowed_origins,
)
}
#[must_use]
pub fn load(
self,
context: RenderContext,
fetch: impl Fetch + 'static,
data: impl Data + 'static,
) -> Processor<Loaded> {
self.load_with(RenderInputs::new(context, fetch, data))
}
fn load_with(self, render: RenderInputs) -> Processor<Loaded> {
self.then(|validated, diagnostics| {
let host = load::Host {
store: validated.store.store(),
fetch: &*render.fetch,
data: &*render.data,
report: validated.edit_context.ambient.report,
};
let data = load::load(
&validated.resolved,
&validated.checks,
&render.context,
&host,
);
diagnostics.extend_from_slice(&data.diagnostics);
Loaded {
validated,
render,
data,
}
})
}
}
impl Processor<Loaded> {
#[must_use]
pub fn inputs(&self) -> &Inputs {
&self.state.data.inputs
}
#[must_use]
pub fn expand(self, options: &expand::Options) -> Processor<Expanded> {
self.then(
|Loaded {
validated,
render,
data,
},
diagnostics| {
let result = expand::expand(
&validated.resolved,
&validated.checks,
&data,
&render.context,
options,
);
diagnostics.extend_from_slice(&result.diagnostics);
let replaced = expand::Replaced {
nodes: 0..0,
inserted: result.root.children.len(),
by: Move::default(),
};
Expanded {
validated,
render,
data,
options: options.clone(),
result,
replaced,
}
},
)
}
#[must_use]
pub fn reload(
self,
context: RenderContext,
fetch: impl Fetch + 'static,
data: impl Data + 'static,
) -> Processor<Loaded> {
self.then(|loaded, diagnostics| back(loaded.validated, diagnostics))
.load(context, fetch, data)
}
}
impl Processor<Expanded> {
#[must_use]
pub fn frontmatter(&self) -> crate::frontmatter::Frontmatter {
let resolved = &self.state.validated.resolved;
crate::frontmatter::of(resolved, resolved.main, &self.state.data)
}
#[must_use]
pub fn components(&self) -> crate::components::Components {
crate::components::of(&self.state.validated.resolved)
}
#[must_use]
pub fn hint_at(&self, offset: usize) -> Option<crate::hints::PositionalHint> {
crate::hints::at(&self.state.validated.resolved, &self.diagnostics, offset)
}
#[must_use]
pub fn tree(&self) -> &tree::Root {
let resolved = &self.state.validated.resolved;
&resolved.document(resolved.main).root
}
#[must_use]
pub fn failed(&self) -> bool {
self.state.options.mode.fails(&self.diagnostics)
}
#[must_use]
pub fn cache_keys(&self) -> Vec<CacheKey> {
let validated = &self.state.validated;
load::cache_keys(
&validated.resolved,
&validated.checks,
&self.state.data.inputs,
)
}
#[must_use]
pub fn pending(&self) -> Pending {
Pending {
packs: self.state.validated.resolved.pending.clone(),
..self.state.data.pending.clone()
}
}
#[must_use]
pub fn expanded(&self) -> &expanded::Root {
&self.state.result.root
}
#[must_use]
pub fn splice(&self) -> Splice {
let replaced = &self.state.replaced;
let from = replaced.nodes.start;
Splice {
from,
to: replaced.nodes.end,
nodes: self.state.result.root.children[from..from + replaced.inserted].to_vec(),
lines: replaced.by.lines,
offset: replaced.by.offset,
}
}
#[must_use]
pub fn reload(
self,
context: RenderContext,
fetch: impl Fetch + 'static,
data: impl Data + 'static,
) -> Processor<Expanded> {
let options = self.state.options.clone();
let before = self.state.result.root.children.len();
let mut processor = self
.then(|expanded, diagnostics| back(expanded.validated, diagnostics))
.load(context, fetch, data)
.expand(&options);
processor.state.replaced.nodes = 0..before;
processor
}
#[must_use]
pub fn host_components(&self) -> Vec<HostComponent> {
let validated = &self.state.validated;
validated.resolved.host_components(validated.store.store())
}
#[must_use]
pub fn sources(&self) -> Needed {
let validated = &self.state.validated;
load::sources(
&validated.resolved,
&validated.checks,
&self.state.render.context,
&validated.allowed_origins,
)
}
#[must_use]
pub fn edit(self, edit: &TextEdit) -> Self {
let Expanded {
validated,
render,
data,
options,
result,
..
} = self.state;
let Validated {
store,
mut resolved,
mut checks,
edit_context: context,
allowed_origins,
..
} = validated;
let before = result.root.children.len();
let (text, mut lines) = resolved.take_text();
let (text, document, change) =
parse::reparse(context.parsed, &text, &mut lines, edit, &context.options);
let Change::Blocks(spliced) = change else {
drop(resolved);
let mut diagnostics = Vec::new();
let parsed = Parsed::new(
context.path,
text,
document,
context.options,
&mut diagnostics,
);
let mut processor = Processor {
diagnostics,
state: parsed,
}
.resolve_in(store, context.ambient, context.policy, context.parses)
.validate(&allowed_origins)
.load_with(render)
.expand(&options);
processor.state.replaced.nodes = 0..before;
return processor;
};
let report = context.ambient.report;
resolved.splice((text, lines), Rc::clone(&document.root), &spliced, report);
let reload = checks.splice(&resolved, &spliced, &allowed_origins);
let mut diagnostics = document.diagnostics.clone();
diagnostics.extend(resolved.diagnostics().cloned());
diagnostics.extend(checks.diagnostics().cloned());
let validated = Validated {
store,
resolved,
checks,
kept: diagnostics.len(),
edit_context: EditContext {
parsed: document,
..context
},
allowed_origins,
};
if reload {
let mut processor = Processor {
diagnostics,
state: validated,
}
.load_with(render)
.expand(&options);
processor.state.replaced.nodes = 0..before;
return processor;
}
let result = expand::splice(
result,
&validated.resolved,
&validated.checks,
&data,
&render.context,
&options,
&spliced,
);
diagnostics.extend_from_slice(&data.diagnostics);
diagnostics.extend_from_slice(&result.diagnostics);
let replaced = result.replaced.clone().unwrap_or(expand::Replaced {
nodes: 0..before,
inserted: result.root.children.len(),
by: Move::default(),
});
Processor {
diagnostics,
state: Expanded {
validated,
render,
data,
options,
result,
replaced,
},
}
}
}
fn back(validated: Validated, diagnostics: &mut Vec<Diagnostic>) -> Validated {
diagnostics.truncate(validated.kept);
validated
}
#[cfg(test)]
mod tests;