use std::path::{Path, PathBuf};
use std::sync::Arc;
use pedant_types::SourceRecord;
use crate::resolution::path_normalization::RelativePathError;
use crate::resolution::read::{ByteCeilings, ReadFault};
use crate::resolution::snapshot_rules::SnapshotRules;
use crate::resolution::snapshot_store::{Interned, SnapshotStoreOf};
use crate::resolution::supply::SourceSupply;
use super::fault::GoSourceFault;
use super::inventory::GoFileInventory;
use super::limits::GoResolutionLimits;
use super::paths;
use super::snapshot_error::GoSnapshotError;
use super::source::GoSource;
pub(super) struct GoSourceStore {
store: SnapshotStoreOf<GoSnapshotRules>,
}
impl GoSourceStore {
pub(super) fn new(root: &Path, limits: GoResolutionLimits) -> Self {
Self {
store: SnapshotStoreOf::new(GoSnapshotRules {
root: root.to_path_buf(),
limits,
}),
}
}
pub(super) fn intern<P: SourceSupply<GoFileInventory, GoSourceFault>>(
&mut self,
provider: &mut P,
canonical: &Path,
) -> Result<(Arc<str>, usize), GoSnapshotError> {
let limits = self.store.rules().limits;
let interned = self
.store
.intern(provider, canonical, &(), |path, record| {
check_retention(record, path, limits)?;
Ok(GoSource::of_record(path, record))
})?;
match interned {
Interned::Held(placement) | Interned::Admitted(placement) => {
Ok((placement.path, placement.index))
}
}
}
pub(super) fn package_name_at(&self, index: usize) -> Option<&str> {
self.store
.stored(index)
.and_then(|source| source.facts().package_name())
}
pub(super) fn finish(self) -> Box<[GoSource]> {
self.store.finish()
}
}
struct GoSnapshotRules {
root: PathBuf,
limits: GoResolutionLimits,
}
impl ByteCeilings for GoSnapshotRules {
fn source_bytes(&self) -> u64 {
self.limits.source_bytes()
}
fn total_bytes(&self) -> u64 {
self.limits.total_bytes()
}
}
impl SnapshotRules for GoSnapshotRules {
type Inventory = GoFileInventory;
type Fault = GoSourceFault;
type Refusal = GoSnapshotError;
type Site = ();
type Stored = GoSource;
fn root(&self) -> &Path {
&self.root
}
fn max_source_files(&self) -> u32 {
self.limits.max_source_files
}
fn stored_path(stored: &GoSource) -> &str {
stored.path()
}
fn unrelative(
&self,
error: RelativePathError,
(_, canonical): (&(), &Path),
) -> GoSnapshotError {
paths::unrelative(error, &self.root, canonical).into()
}
fn source_files(&self, (_, _): (&(), &str)) -> GoSnapshotError {
GoSnapshotError::SourceFileLimitExceeded {
limit: self.limits.max_source_files,
}
}
fn unnormalized(&self, (_, relative): (&(), &str)) -> GoSnapshotError {
GoSnapshotError::UnnormalizedPath {
path: Box::from(relative),
}
}
fn refused(&self, fault: GoSourceFault, (_, relative): (&(), &str)) -> GoSnapshotError {
refusal(fault, (&self.root, relative))
}
fn oversized(&self, fault: ReadFault, at: (&(), &str)) -> GoSnapshotError {
self.refused(GoSourceFault::of_read(fault, self.limits), at)
}
}
fn check_retention(
record: &SourceRecord<GoFileInventory>,
relative: &str,
limits: GoResolutionLimits,
) -> Result<(), GoSnapshotError> {
let facts = record.facts();
match (
facts.fact_count() > limits.max_facts_per_source,
facts.syntax_depth() > limits.max_syntax_depth,
) {
(true, _) => Err(GoSnapshotError::RetainedFactsExceeded {
path: Box::from(relative),
limit: limits.max_facts_per_source,
}),
(false, true) => Err(GoSnapshotError::RetainedDepthExceeded {
path: Box::from(relative),
limit: limits.max_syntax_depth,
}),
(false, false) => Ok(()),
}
}
fn refusal(fault: GoSourceFault, at: (&Path, &str)) -> GoSnapshotError {
let (root, relative) = at;
match fault {
GoSourceFault::Unreadable(source) => GoSnapshotError::SourceRead {
path: Box::from(relative),
source,
},
GoSourceFault::OutOfRoot { path } => GoSnapshotError::OutOfRoot {
path,
root: paths::path_text(root),
request: Box::from(relative),
},
GoSourceFault::NonUtf8Path { path } => GoSnapshotError::NonUtf8Path { path },
GoSourceFault::PathRead { path, source } => GoSnapshotError::PathRead { path, source },
GoSourceFault::SourceFiles { ceiling } => {
GoSnapshotError::SourceFileLimitExceeded { limit: ceiling }
}
GoSourceFault::SourceBytes { ceiling } => GoSnapshotError::SourceBytesLimitExceeded {
path: Box::from(relative),
limit: ceiling,
},
GoSourceFault::TotalBytes { ceiling } => {
GoSnapshotError::TotalSourceBytesLimitExceeded { limit: ceiling }
}
GoSourceFault::NonUtf8 { reason } => GoSnapshotError::NonUtf8Source {
path: Box::from(relative),
reason,
},
GoSourceFault::Unparsed { reason } => GoSnapshotError::UnparsedSource {
path: Box::from(relative),
reason,
},
GoSourceFault::Incomplete(defect) => GoSnapshotError::IncompleteSource {
path: Box::from(relative),
defect,
},
GoSourceFault::FactExtraction(source) => GoSnapshotError::FactExtraction {
path: Box::from(relative),
source,
},
GoSourceFault::MissingPackageClause => GoSnapshotError::MissingPackageClause {
path: Box::from(relative),
},
GoSourceFault::Refused { path } => GoSnapshotError::AlreadyRefused { path },
GoSourceFault::StructureProjection { reason } => GoSnapshotError::StructureProjection {
path: Box::from(relative),
reason,
},
}
}