use crate::ident::{Descriptor, Ident, Locator};
use crate::scan::{
EntryRec, META_BUILT, META_OPTIONAL, META_UNPLUGGED, NameRange, PackedMeta, Range32, Span,
unpack_meta,
};
use crate::table::{SegTabs, Slabs};
use std::fmt;
use std::iter::FusedIterator;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum LinkType {
Hard,
Soft,
}
impl TryFrom<&str> for LinkType {
type Error = ();
fn try_from(s: &str) -> Result<Self, Self::Error> {
match s {
"hard" => Ok(Self::Hard),
"soft" => Ok(Self::Soft),
_ => Err(()),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct DependencyMeta {
pub built: Option<bool>,
pub optional: Option<bool>,
pub unplugged: Option<bool>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct PeerDependencyMeta {
pub optional: bool,
}
#[inline]
fn table<T>(v: &[T], r: Range32) -> &[T] {
v.get(r.0 as usize..r.1 as usize).unwrap_or(&[])
}
#[inline]
fn descriptor<'l>(src: &'l str, d: &NameRange) -> Descriptor<'l> {
let s = d.name.start as usize;
let e = (d.range.start + d.range.len) as usize;
Descriptor::from_text(
src.get(s..e).unwrap_or(""),
d.name.len,
d.range.start.saturating_sub(d.name.start),
)
}
#[inline]
fn dependency_meta(m: PackedMeta) -> DependencyMeta {
DependencyMeta {
built: unpack_meta(m, META_BUILT),
optional: unpack_meta(m, META_OPTIONAL),
unplugged: unpack_meta(m, META_UNPLUGGED),
}
}
#[inline]
fn peer_dependency_meta(m: PackedMeta) -> PeerDependencyMeta {
PeerDependencyMeta {
optional: unpack_meta(m, META_OPTIONAL) == Some(true),
}
}
#[derive(Clone, Copy)]
pub struct EntryView<'l> {
src: &'l str,
slabs: &'l Slabs,
tabs: &'l SegTabs,
rec: &'l EntryRec,
}
impl<'l> EntryView<'l> {
#[inline]
pub(crate) const fn new(
src: &'l str,
slabs: &'l Slabs,
tabs: &'l SegTabs,
rec: &'l EntryRec,
) -> Self {
Self {
src,
slabs,
tabs,
rec,
}
}
#[inline]
#[must_use]
pub fn descriptors(&self) -> Descriptors<'l> {
Descriptors {
src: self.src,
items: table(self.slabs.descs(self.tabs), self.rec.descs),
}
}
#[inline]
#[must_use]
pub fn version(&self) -> Option<&'l str> {
self.rec.version.opt(self.src)
}
#[inline]
#[must_use]
pub fn resolution(&self) -> Option<&'l str> {
self.rec.resolution.opt(self.src)
}
#[inline]
#[must_use]
pub fn resolution_locator(&self) -> Option<Locator<'l>> {
self.resolution().and_then(Locator::from_resolution)
}
#[inline]
#[must_use]
pub fn language_name(&self) -> &'l str {
self.rec.language.opt(self.src).unwrap_or("unknown")
}
#[inline]
#[must_use]
pub const fn link_type(&self) -> LinkType {
if self.rec.link_soft {
LinkType::Soft
} else {
LinkType::Hard
}
}
#[inline]
#[must_use]
pub fn checksum(&self) -> Option<&'l str> {
self.rec.checksum.opt(self.src)
}
#[inline]
#[must_use]
pub fn conditions(&self) -> Option<&'l str> {
self.rec.conditions.opt(self.src)
}
#[inline]
#[must_use]
pub fn dependencies(&self) -> Dependencies<'l> {
Dependencies {
src: self.src,
items: table(self.slabs.deps(self.tabs), self.rec.deps),
}
}
#[inline]
#[must_use]
pub fn peer_dependencies(&self) -> Dependencies<'l> {
Dependencies {
src: self.src,
items: table(self.slabs.peers(self.tabs), self.rec.peers),
}
}
#[inline]
#[must_use]
pub fn bin(&self) -> Bins<'l> {
Bins {
src: self.src,
items: table(self.slabs.bins(self.tabs), self.rec.bins),
}
}
#[inline]
#[must_use]
pub fn dependencies_meta(&self) -> DependenciesMeta<'l> {
DependenciesMeta {
src: self.src,
items: table(self.slabs.dmeta(self.tabs), self.rec.dmeta),
}
}
#[inline]
#[must_use]
pub fn peer_dependencies_meta(&self) -> PeerDependenciesMeta<'l> {
PeerDependenciesMeta {
src: self.src,
items: table(self.slabs.pmeta(self.tabs), self.rec.pmeta),
}
}
}
impl fmt::Debug for EntryView<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Entry")
.field("descriptors", &self.descriptors())
.field("version", &self.version())
.field("resolution", &self.resolution())
.field("language_name", &self.language_name())
.field("link_type", &self.link_type())
.field("checksum", &self.checksum())
.field("conditions", &self.conditions())
.field("dependencies", &self.dependencies())
.field("peer_dependencies", &self.peer_dependencies())
.field("bin", &self.bin())
.field("dependencies_meta", &self.dependencies_meta())
.field("peer_dependencies_meta", &self.peer_dependencies_meta())
.finish()
}
}
macro_rules! list_view {
(
$(#[$doc:meta])* $name:ident, $iter:ident, $rec:ty, $item:ty,
|$src:ident, $r:ident| $make:expr
) => {
$(#[$doc])*
#[derive(Clone, Copy)]
pub struct $name<'l> {
src: &'l str,
items: &'l [$rec],
}
impl<'l> $name<'l> {
#[inline]
#[must_use]
pub const fn len(&self) -> usize {
self.items.len()
}
#[inline]
#[must_use]
pub const fn is_empty(&self) -> bool {
self.items.is_empty()
}
#[inline]
#[must_use]
pub fn iter(&self) -> $iter<'l> {
$iter {
src: self.src,
inner: self.items.iter(),
}
}
#[inline]
#[allow(clippy::redundant_closure_call)]
fn make($src: &'l str, $r: &'l $rec) -> $item {
$make
}
}
impl<'l> IntoIterator for $name<'l> {
type Item = $item;
type IntoIter = $iter<'l>;
#[inline]
fn into_iter(self) -> $iter<'l> {
self.iter()
}
}
impl<'l> IntoIterator for &$name<'l> {
type Item = $item;
type IntoIter = $iter<'l>;
#[inline]
fn into_iter(self) -> $iter<'l> {
self.iter()
}
}
impl fmt::Debug for $name<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_list().entries(self.iter()).finish()
}
}
#[doc = concat!("Iterator over a [`", stringify!($name), "`].")]
#[derive(Clone)]
pub struct $iter<'l> {
src: &'l str,
inner: std::slice::Iter<'l, $rec>,
}
impl<'l> Iterator for $iter<'l> {
type Item = $item;
#[inline]
fn next(&mut self) -> Option<$item> {
let src = self.src;
self.inner.next().map(|r| $name::make(src, r))
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
self.inner.size_hint()
}
#[inline]
fn nth(&mut self, n: usize) -> Option<$item> {
let src = self.src;
self.inner.nth(n).map(|r| $name::make(src, r))
}
}
impl DoubleEndedIterator for $iter<'_> {
#[inline]
fn next_back(&mut self) -> Option<Self::Item> {
let src = self.src;
self.inner.next_back().map(|r| $name::make(src, r))
}
}
impl ExactSizeIterator for $iter<'_> {}
impl FusedIterator for $iter<'_> {}
impl fmt::Debug for $iter<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct(stringify!($iter))
.field("remaining", &self.inner.len())
.finish()
}
}
};
}
list_view!(
Descriptors, DescriptorIter, NameRange, Descriptor<'l>,
|src, d| descriptor(src, d)
);
list_view!(
Dependencies, DependencyIter, NameRange, Descriptor<'l>,
|src, d| descriptor(src, d)
);
list_view!(
Bins, BinIter, NameRange, (&'l str, &'l str),
|src, b| (b.name.get(src), b.range.get(src))
);
list_view!(
DependenciesMeta, DependencyMetaIter, (Span, PackedMeta), (Ident<'l>, DependencyMeta),
|src, m| (Ident::new(m.0.get(src)), dependency_meta(m.1))
);
list_view!(
PeerDependenciesMeta, PeerDependencyMetaIter, (Span, PackedMeta), (Ident<'l>, PeerDependencyMeta),
|src, m| (Ident::new(m.0.get(src)), peer_dependency_meta(m.1))
);
impl<'l> Descriptors<'l> {
#[inline]
#[must_use]
pub fn get(&self, i: usize) -> Option<Descriptor<'l>> {
self.items.get(i).map(|d| descriptor(self.src, d))
}
#[inline]
#[must_use]
pub fn first(&self) -> Option<Descriptor<'l>> {
self.get(0)
}
}
impl<'l> Dependencies<'l> {
#[inline]
fn name(&self, d: &NameRange) -> &'l str {
d.name.get(self.src)
}
#[inline]
#[must_use]
pub fn get(&self, name: &str) -> Option<Descriptor<'l>> {
self
.items
.iter()
.rev()
.find(|d| self.name(d) == name)
.map(|d| descriptor(self.src, d))
}
#[must_use]
pub fn last_wins(&self) -> LastWins<'l> {
LastWins::new(*self)
}
#[must_use]
pub fn lookup(&self) -> DependencyLookup<'l> {
DependencyLookup::new(*self)
}
}
impl<'l> Bins<'l> {
#[inline]
#[must_use]
pub fn get(&self, name: &str) -> Option<&'l str> {
self
.items
.iter()
.rev()
.find(|b| b.name.get(self.src) == name)
.map(|b| b.range.get(self.src))
}
}
impl DependenciesMeta<'_> {
#[inline]
#[must_use]
pub fn get(&self, name: &str) -> Option<DependencyMeta> {
self
.items
.iter()
.rev()
.find(|m| m.0.get(self.src) == name)
.map(|m| dependency_meta(m.1))
}
}
impl PeerDependenciesMeta<'_> {
#[inline]
#[must_use]
pub fn get(&self, name: &str) -> Option<PeerDependencyMeta> {
self
.items
.iter()
.rev()
.find(|m| m.0.get(self.src) == name)
.map(|m| peer_dependency_meta(m.1))
}
}
pub struct LastWins<'l> {
deps: Dependencies<'l>,
pos: usize,
keep: Option<Vec<bool>>,
}
impl<'l> LastWins<'l> {
fn new(deps: Dependencies<'l>) -> Self {
let items = deps.items;
let sorted = items
.windows(2)
.all(|w| deps.name(&w[0]) < deps.name(&w[1]));
let keep = (!sorted).then(|| {
let mut order: Vec<usize> = (0..items.len()).collect();
order.sort_unstable_by(|&a, &b| {
deps
.name(&items[a])
.cmp(deps.name(&items[b]))
.then(b.cmp(&a))
});
let mut flags = vec![false; items.len()];
let mut prev: Option<&str> = None;
for i in order {
let name = deps.name(&items[i]);
if prev != Some(name) {
flags[i] = true;
prev = Some(name);
}
}
flags
});
Self { deps, pos: 0, keep }
}
}
impl<'l> Iterator for LastWins<'l> {
type Item = Descriptor<'l>;
fn next(&mut self) -> Option<Descriptor<'l>> {
while self.pos < self.deps.items.len() {
let i = self.pos;
self.pos += 1;
if self.keep.as_ref().is_none_or(|k| k[i]) {
return Some(descriptor(self.deps.src, &self.deps.items[i]));
}
}
None
}
fn size_hint(&self) -> (usize, Option<usize>) {
let rest = self.deps.items.len() - self.pos;
let min = if self.keep.is_none() {
rest
} else {
usize::from(rest > 0)
};
(min, Some(rest))
}
}
impl FusedIterator for LastWins<'_> {}
impl fmt::Debug for LastWins<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("LastWins")
.field("remaining", &(self.deps.items.len() - self.pos))
.finish_non_exhaustive()
}
}
#[derive(Clone)]
pub struct DependencyLookup<'l> {
src: &'l str,
lines: Vec<&'l NameRange>,
}
impl<'l> DependencyLookup<'l> {
fn new(deps: Dependencies<'l>) -> Self {
let src = deps.src;
let mut lines: Vec<&'l NameRange> = deps.items.iter().collect();
lines.sort_unstable_by(|a, b| {
a.name
.get(src)
.cmp(b.name.get(src))
.then(b.name.start.cmp(&a.name.start))
});
lines.dedup_by(|later, kept| later.name.get(src) == kept.name.get(src));
Self { src, lines }
}
#[must_use]
pub fn get(&self, name: &str) -> Option<Descriptor<'l>> {
self
.lines
.binary_search_by(|d| d.name.get(self.src).cmp(name))
.ok()
.map(|i| descriptor(self.src, self.lines[i]))
}
#[must_use]
pub fn len(&self) -> usize {
self.lines.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.lines.is_empty()
}
pub fn iter(&self) -> impl ExactSizeIterator<Item = Descriptor<'l>> + '_ {
self.lines.iter().map(|d| descriptor(self.src, d))
}
}
impl fmt::Debug for DependencyLookup<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_list().entries(self.iter()).finish()
}
}