/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

use std::borrow::Borrow;
use std::cell::RefCell;
use std::collections::HashMap;
use std::fmt;
use std::fmt::Debug;
use std::hash::Hash;
use std::hash::Hasher;
use std::num::NonZeroU32;
use std::sync::atomic::AtomicU32;
use std::sync::atomic::Ordering;
use std::u32;

use once_cell::sync::OnceCell;
use serde::Deserialize;
use serde::Deserializer;
use serde::Serialize;
use serde::Serializer;

use crate::atomic_arena;
use crate::atomic_arena::AtomicArena;
use crate::idhasher::BuildIdHasher;
use crate::sharded_set::ShardedSet;

/// `InternId`s wrap the `Ref<T>` type.
#[doc(hidden)]
pub type Ref<T> = atomic_arena::Ref<'static, T>;

/// The `InternId` trait is applied to the identifiers of interned data, and
/// `InternId::Intern` is the type of the data that was interned.  You should
/// not implement `InternId` manually; instead, use the [intern_struct] macro.
pub trait InternId: 'static + Eq + Copy {
    /// Actual type of the interned data.
    type Intern: Hash + Eq + 'static + Borrow<Self::Lookup>;
    /// Type by which interned data can be looked up.  This lets us support
    /// (for example) looking up a `StringId` using `&str` in `get_interned`.
    /// In most cases it will be the same as `Intern`.
    type Lookup: Hash + Eq + ?Sized;

    /// Return the static intern table for this type, which encapsulates the
    /// mapping from `InternId`s to their corresponding data.
    fn table() -> &'static InternTable<Self, Self::Intern>;

    /// Wrap and unwrap references.
    #[doc(hidden)]
    fn wrap(r: Ref<Self::Intern>) -> Self;

    #[doc(hidden)]
    fn unwrap(self) -> Ref<Self::Intern>;

    // Methods from here on are implemented for you,
    // and you should not override them.

    /// Intern a value.
    fn intern<U>(t: U) -> Self
    where
        U: Into<Self::Intern> + Borrow<Self::Lookup>,
        AsInterned<Self>: Borrow<Self::Lookup>,
    {
        Self::table().intern(t)
    }

    /// Fetch an existing interned value if it exists.  You can use
    /// any type that you would use for a hash table lookup keyed by
    /// `Self::Intern`; by contrast `intern()` requires that you be able
    /// to convert the key `Into<Self::Intern>` if it hasn't been
    /// interned yet.
    fn get_interned<U>(t: &U) -> Option<Self>
    where
        U: Borrow<Self::Lookup>,
        AsInterned<Self>: Borrow<Self::Lookup>,
    {
        Self::table().get_interned(t)
    }

    /// Given an `InternId`, retrieve the corresponding data.  Equivalent to
    /// `Deref::deref`, but specifies the resulting reference is `'static`.
    #[inline]
    fn get(self) -> &'static Self::Intern {
        Self::table().get(self)
    }

    /// 0-based index of interned value among all interned values of this type,
    /// suitable for indexing arrays with interned values.
    #[inline]
    fn index(self) -> u32 {
        self.unwrap().index()
    }

    /// Reverse the results of `index()`, and check the index boundary.
    #[inline]
    fn from_index_checked(i: u32) -> Option<Self> {
        if (i as usize) < Self::table().len() {
            // This is safe because `from_index` uses `NonZeroU32::new_unchecked`
            // and the value is always > 0
            let ref_ = unsafe { Ref::from_index(i) };
            Some(Self::wrap(ref_))
        } else {
            None
        }
    }

    /// Unsafely reverse the results of `index()`.
    #[doc(hidden)]
    #[inline]
    unsafe fn from_index(i: u32) -> Self {
        Self::wrap(Ref::from_index(i))
    }

    /// Raw index for internal use only.
    #[doc(hidden)]
    #[inline]
    fn raw(self) -> NonZeroU32 {
        self.unwrap().raw()
    }

    /// Unsafely reverse the results of `raw()`.
    #[doc(hidden)]
    #[inline]
    unsafe fn from_raw(i: NonZeroU32) -> Self {
        Self::wrap(Ref::from_raw(i))
    }
}

/// There are two very different ways you might want to use an `InternId` as a
/// hash table key.  The default if you `derive(Hash, PartialEq, Eq)` is to use
/// the representation of the `Id` itself (roughly equivalent to `.index()`) as
/// the hash code.  This yields fast equality and hash, but you can only look
/// up interned values.  The default `Set` and `Map` types created by the macro
/// use this hash code with the custom fast hasher
/// `idhasher::BuildIdHasher<T>`.
///
/// `AsInterned` is a simple wrapper that supports the opposite view:
/// treat the `InternId` as a reference to `InternId::Intern` and use the
/// hash code of `InternId::Intern`.  This allows you to compare to
/// uninterned data, by comparing the results of `InternId::get`.
/// `AsInterned` is public so that it can be used if you need it (for
/// example if you're using a mix of interned and uninterned values to
/// avoid interning intermediate data that you want to throw away).
///
/// It's unsafe to `impl Borrow<InternId::Intern> for InternId`; `Borrow`
/// requires hash codes to be equal.  You can't directly use `InternId` as a
/// hash table key and then expect to be able to use `InternId::Intern` to
/// perform lookups without interning.
/// An explicit `impl Borrow<InternId::Intern> for AsInterned<InternId>` is
/// still required for every `InternId`.  The [intern_struct] macro provides
/// this implementation as it's used internally for the intern process itself.
#[derive(Debug, Copy, Clone)]
#[repr(transparent)]
pub struct AsInterned<Id>(pub Id);

impl<Id: InternId> Hash for AsInterned<Id> {
    fn hash<H: Hasher>(&self, h: &mut H) {
        self.0.get().hash(h)
    }
}

impl<Id: InternId> PartialEq for AsInterned<Id> {
    fn eq(&self, other: &Self) -> bool {
        // Note: intern yields equal ids *iff* the underlying equality did so,
        // and the underlying hashing is consistent with equality (which is an
        // invariant of Hash).  That's because the underlying equality and hash
        // are used when the data is interned in the first place.  Thus this is
        // safe.
        self.0 == other.0
    }
}

impl<Id: InternId> Eq for AsInterned<Id> {}

impl<Id> Ord for AsInterned<Id>
where
    Id: InternId,
    Id::Intern: Ord,
{
    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
        self.0.get().cmp(other.0.get())
    }
}

impl<Id> PartialOrd for AsInterned<Id>
where
    Id: InternId,
    Id::Intern: PartialOrd,
{
    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
        self.0.get().partial_cmp(other.0.get())
    }
}

type Shards<Id> = ShardedSet<AsInterned<Id>, std::hash::BuildHasherDefault<fnv::FnvHasher>>;

/// An `InternTable` manages all the storage associated with the `Id`.
#[derive(Default)]
pub struct InternTable<Id, Type> {
    shards: OnceCell<Shards<Id>>,      // Allocated lazily.
    arena: AtomicArena<'static, Type>, // Static.
    serdes_type_index: AtomicU32,      // Initialized lazily.
}

static NEXT_SERDES_TYPE_INDEX: AtomicU32 = AtomicU32::new(1);

impl<Id, Type> InternTable<Id, Type> {
    /// Create new `InternTable`
    #[doc(hidden)]
    pub const fn new() -> Self {
        InternTable {
            shards: OnceCell::new(),
            arena: AtomicArena::new(),
            serdes_type_index: AtomicU32::new(u32::MAX),
        }
    }

    /// Create new `InternTable` with a distinguished constant as the first
    /// interned value.
    #[doc(hidden)]
    pub const fn with_zero(z: &'static atomic_arena::Zero<Type>) -> Self {
        InternTable {
            shards: OnceCell::new(),
            arena: AtomicArena::with_zero(z),
            serdes_type_index: AtomicU32::new(u32::MAX),
        }
    }

    /// Yields the current count of interned values; this can be used
    /// in conjunction with `.index()` on those values to allocate and
    /// index a direct-mapped array.
    pub fn len(&self) -> usize {
        self.arena.len()
    }

    pub fn is_empty(&self) -> bool {
        self.arena.is_empty()
    }
}

impl<Id: InternId> InternTable<Id, Id::Intern> {
    /// The methods from here on are internal and private.
    fn shards(&'static self) -> &Shards<Id> {
        self.shards.get_or_init(|| {
            let shards: Shards<Id> = ShardedSet::default();
            if !self.arena.is_empty() {
                let iwz = AsInterned(Id::wrap(atomic_arena::Zero::zero()));
                shards.unchecked_insert(iwz);
            }
            shards
        })
    }

    /// Intern `t`, and return the resulting `Id`.
    fn intern<U>(&'static self, t: U) -> Id
    where
        U: Into<Id::Intern> + Borrow<Id::Lookup>,
        AsInterned<Id>: Borrow<Id::Lookup>,
    {
        let wt = t.borrow();
        // Optimistically try to write lock (because it only requires
        // one lock acquisition check for the new-string case), but if
        // there's write contention take a slower path.
        let shards = self.shards();
        let mut insert_lock = match shards.get_or_insert_lock(wt) {
            Ok(AsInterned(id)) => return id,
            Err(insert_lock) => insert_lock,
        };
        let id = Id::wrap(self.arena.add(t.into()));
        insert_lock.insert(AsInterned(id));
        id
    }

    /// If `t` has already been interned, return the corresponding `Id`.
    /// Note that this only borrows `t`, and thus avoids
    /// creating an `Id::Intern`.
    fn get_interned<U>(&'static self, t: &U) -> Option<Id>
    where
        U: Borrow<Id::Lookup>,
        AsInterned<Id>: Borrow<Id::Lookup>,
    {
        if let Some(AsInterned(id)) = self.shards().get(t.borrow()) {
            Some(id)
        } else {
            None
        }
    }

    /// Get a shared reference to the underlying `Id::Intern`.
    /// Usually you can rely on `deref` to do this implicitly.
    #[inline]
    fn get(&'static self, r: Id) -> &Id::Intern {
        &*self.arena.get(r.unwrap())
    }

    /// Getter that checks for the need to allocate.
    fn serdes_type_index(&'static self) -> u32 {
        let i = self.serdes_type_index.load(Ordering::Acquire);
        if i != u32::MAX {
            i
        } else {
            self.serdes_type_index_slow()
        }
    }

    fn serdes_type_index_slow(&'static self) -> u32 {
        let i = NEXT_SERDES_TYPE_INDEX.fetch_add(1, Ordering::Relaxed);
        assert!(i != u32::MAX); // Or we've overflowed.
        // Now, we might be racing another thread to assign self.type_index.
        // So CAS it in, keeping any entry that was already there (since it's
        // already being used).
        if let Err(winner) = self.serdes_type_index.compare_exchange(
            u32::MAX,
            i,
            Ordering::AcqRel,
            Ordering::Acquire,
        ) {
            winner
        } else {
            i
        }
    }
}

impl<Id, Type> Debug for InternTable<Id, Type> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "InternTable[{} entries]", self.len())
    }
}

// `PerInternIdVec` can be used to allocate a data structure for each
// distinct `Id` type.  Note, however, that the underlying type `T` has to
// be the same for all `Id` types.
#[derive(Default, Debug)]
struct PerInternIdVec<T> {
    v: Vec<T>,
}

impl<T: Default> PerInternIdVec<T> {
    /// Ensure there is an element for all currently-extant `Id` types,
    /// extending with default elements if they are missing.
    fn ensure_default(&mut self) {
        let n = NEXT_SERDES_TYPE_INDEX.load(Ordering::Relaxed);
        if n as usize > self.v.len() {
            self.v.resize_with(n as usize, T::default);
        }
    }

    /// Discard all memory.
    fn clear(&mut self) {
        self.v = Default::default();
    }

    // Make sure there's an entry for `Id` and return its index.
    fn ensure_and_index<Id: InternId>(&mut self) -> u32 {
        let i = Id::table().serdes_type_index(); // May increment NEXT_SERDES_TYPE_INDEX, do first.
        self.ensure_default();
        i
    }

    fn for_id<Id: InternId>(&mut self) -> &T {
        let i = self.ensure_and_index::<Id>();
        &self.v[i as usize]
    }

    fn for_id_mut<Id: InternId>(&mut self) -> &mut T {
        let i = self.ensure_and_index::<Id>();
        &mut self.v[i as usize]
    }
}

#[derive(Default)]
struct SerState {
    next_index: u32,
    ref_to_index: HashMap<Ref<usize>, u32, BuildIdHasher<u32>>,
}

thread_local! {
    static REF_TO_INDEX: RefCell<PerInternIdVec<SerState>> = Default::default();
    static INDEX_TO_REF: RefCell<PerInternIdVec<Vec<Ref<usize>>>> = Default::default();
}

/// Create a `SerGuard::default()` before serializing types that transitively
/// contain `InternId`s, then drop the resulting `SerGuard` when serialization
/// is complete.
#[derive(Debug)]
pub struct SerGuard {
    _v: (), // Prevent construction except via Default
}

impl Default for SerGuard {
    fn default() -> Self {
        REF_TO_INDEX.with(|rti| rti.borrow_mut().ensure_default());
        SerGuard { _v: () }
    }
}

impl Drop for SerGuard {
    fn drop(&mut self) {
        REF_TO_INDEX.with(|rti| rti.borrow_mut().clear());
    }
}

/// Create a `DeGuard::default()` before deserializing types that transitively
/// contain `InternId`s, then drop the resulting `DeGuard` when deserialization
/// is complete.
#[derive(Debug)]
pub struct DeGuard {
    _v: (), // Prevent construction except via Default
}

impl Default for DeGuard {
    fn default() -> Self {
        INDEX_TO_REF.with(|itr| itr.borrow_mut().ensure_default());
        DeGuard { _v: () }
    }
}

impl Drop for DeGuard {
    fn drop(&mut self) {
        INDEX_TO_REF.with(|itr| itr.borrow_mut().clear());
    }
}

/// Wrap an object in WithIntern to set the necessary context to
/// serialize the interned objects it contains.
#[derive(Debug)]
pub struct WithIntern<T>(pub T);

impl<T> WithIntern<T> {
    pub fn strip<E>(t: Result<Self, E>) -> Result<T, E> {
        t.map(|WithIntern(r)| r)
    }
}

impl<T: Serialize> Serialize for WithIntern<T> {
    fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
        let _guard = SerGuard::default();
        self.0.serialize(s)
    }
}

impl<'de, T: Deserialize<'de>> Deserialize<'de> for WithIntern<T> {
    fn deserialize<D: Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
        let _guard = DeGuard::default();
        Ok(WithIntern(T::deserialize(d)?))
    }
}

/// `InternId`s do serdes via `InternSerdes`.
#[derive(Debug)]
#[repr(transparent)]
pub struct InternSerdes<Id: InternId>(pub Id);

impl<Id: InternId> From<Id> for InternSerdes<Id> {
    fn from(id: Id) -> Self {
        InternSerdes(id)
    }
}

#[derive(Debug, Serialize, Deserialize)]
enum InternEnum<T> {
    Value(T),
    Id(u32),
}

impl<Id> Serialize for InternSerdes<Id>
where
    Id: InternId,
    Id::Intern: Serialize,
{
    fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
        // strip the actual type off self's internal Ref.
        let r: Ref<usize> = unsafe { self.0.unwrap().rebrand() };
        debug_assert_eq!(r.index(), self.0.unwrap().index());
        REF_TO_INDEX.with(|cell| {
            let mut tls = cell.borrow_mut();
            let state = tls.for_id::<Id>();
            let opt_ser_id = state.ref_to_index.get(&r).copied();
            drop(tls);

            if let Some(ser_id) = opt_ser_id {
                let ie: InternEnum<&Id::Intern> = InternEnum::Id(ser_id);
                ie.serialize(s)
            } else {
                let ie: InternEnum<&Id::Intern> = InternEnum::Value(self.0.get());
                let res = ie.serialize(s)?;

                let mut tls = cell.borrow_mut();
                let state = tls.for_id_mut::<Id>();

                // Grab the next backref index. This is not necessarily the same as
                // `ref_to_index.len()` in certain recursive cases. Incrementing a simple
                // counter matches the index numbering the deserializer expects.
                let index = state.next_index;
                state.next_index = index + 1;

                // Associate this Ref with the new index, for back references.  However, if it
                // already got associated with a back reference when we recursively serialized
                // the value above, just leave it alone. Either index will work in the
                // deserializer, since it just deserialized (and interned) the same value
                // twice, but we might as well consistently use the lowest-numbered ID.
                state.ref_to_index.entry(r).or_insert(index);

                Ok(res)
            }
        })
    }
}

impl<'de, Id> Deserialize<'de> for InternSerdes<Id>
where
    Id: InternId,
    Id::Intern: Deserialize<'de>,
    AsInterned<Id>: Borrow<Id::Lookup>,
{
    fn deserialize<D: Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
        let ie: InternEnum<Id::Intern> = InternEnum::deserialize(d)?;
        match ie {
            InternEnum::Value(w) => {
                let id: Id = Id::intern(w);
                let r: Ref<usize> = unsafe { id.unwrap().rebrand() };
                debug_assert_eq!(r.index(), id.unwrap().index());
                INDEX_TO_REF.with(|itr| itr.borrow_mut().for_id_mut::<Id>().push(r));
                Ok(InternSerdes(id))
            }
            InternEnum::Id(i) => {
                let r: Ref<usize> =
                    INDEX_TO_REF.with(|itr| itr.borrow_mut().for_id_mut::<Id>()[i as usize]);
                let id: Id = Id::wrap(unsafe { r.rebrand() });
                debug_assert_eq!(r.index(), id.unwrap().index());
                Ok(InternSerdes(id))
            }
        }
    }
}

/// The body of intern_struct! is a sequence of declarations (the ? lines are optional):
/// ```ignore
///     #[attr]*
///     [pub] struct IdTypeName = Intern<InternType> {
///         serdes("intern::InternSerdes<IdTypeName>"); ?  // Enables serdes
///         type Lookup = LookupType; ?                    // Intern lookup at LookupType
///         type Set = IdSetName; ?                        // Name of set type
///         type Map = IdMapName; ?                        // Name of map type
///         const ZERO_NAME = idtype_expr; ?               // Zero element
///     }
/// ```
#[macro_export]
macro_rules! intern_struct {
    () => { };
    ($(#[$attr:meta])* struct $Name:ident = Intern<$T:ty> {
        $(serdes($l:expr);)?
        $(type Lookup = $L:ty;)?
        $(type Set = $S:ident;)?
        $(type Map = $M:ident;)?
        $(const $Z:ident = $ze:expr;)?
     }
     $($rest:tt)*) => {
        intern_struct!(@DOIT, ($(#[$attr])*), (), $Name, $T,
                       ( $($L)? ), ( $($Z, $ze)? ), ( $($l)? ), ( $($S)? ), ( $($M)? ) );
        intern_struct!{ $($rest)* }
    };
    ($(#[$attr:meta])* pub struct $Name:ident = Intern<$T:ty> {
        $(serdes($l:expr);)?
        $(type Lookup = $L:ty;)?
        $(type Set = $S:ident;)?
        $(type Map = $M:ident;)?
        $(const $Z:ident = $ze:expr;)?
     }
     $($rest:tt)*) => {
        intern_struct!(@DOIT, ($(#[$attr])*), (pub), $Name, $T,
                       ( $($L)? ), ( $($Z, $ze)? ), ( $($l)? ), ( $($S)? ), ( $($M)? ) );
        intern_struct!{ $($rest)* }
    };
    ($(#[$attr:meta])* pub(crate) struct $Name:ident = Intern<$T:ty> {
        $(serdes($l:expr);)?
        $(type Lookup = $L:ty;)?
        $(type Set = $S:ident;)?
        $(type Map = $M:ident;)?
        $(const $Z:ident = $ze:expr;)?
     }
     $($rest:tt)*) => {
        intern_struct!(@DOIT, ($(#[$attr])*), (pub(crate)), $Name, $T,
                       ( $($L)? ), ( $($Z, $ze)? ), ( $($l)? ), ( $($S)? ), ( $($M)? ) );
        intern_struct!{ $($rest)* }
    };
    (@SERDESDERIVE(); $($decl:tt)*) => { $($decl)* };
    (@SERDESDERIVE($l:expr); $($decl:tt)*) => {
        #[derive(serde_derive::Deserialize, serde_derive::Serialize)]
        #[serde(from = $l)]
        #[serde(into = $l)]
        $($decl)*
    };
    (@LOOKUPTYPE($T:ty, ())) => { type Lookup = $T; };
    (@LOOKUPTYPE($T:ty, ($Lookup:ty))) => { type Lookup = $Lookup; };
    (@BORROW($T:ty, $Name:ty, ())) => {
        impl std::borrow::Borrow<$T> for $crate::intern::AsInterned<$Name> {
            #[inline]
            fn borrow(&self) -> & $T {
                use $crate::intern::InternId;
                self.0.get()
            }
        }
    };
    (@BORROW($T:ty, $Name:ty, ($Lookup:ty))) => {
        impl std::borrow::Borrow<$Lookup> for $crate::intern::AsInterned<$Name> {
            #[inline]
            fn borrow(&self) -> & $Lookup {
                use $crate::intern::InternId;
                self.0.get().borrow()
            }
        }
    };
    (@MAP(($($vis:tt)*), $Name:ident, ())) => { };
    (@MAP(($($vis:tt)*), $Name:ident, ($M:ident))) => {
        $($vis)* type $M<V> = std::collections::HashMap<$Name, V, $crate::idhasher::BuildIdHasher<u32>>;
    };
    (@SET(($($vis:tt)*), $Name:ident, ())) => { };
    (@SET(($($vis:tt)*), $Name:ident, ($S:ident))) => {
        $($vis)* type $S = std::collections::HashSet<$Name, $crate::idhasher::BuildIdHasher<u32>>;
    };
    (@TABLE($T:ty, ())) => { $crate::intern::InternTable::new() };
    (@TABLE($T:ty, ($v:ident, $zero:expr))) => {{
        static ZERO: $crate::Zero<$T> = $crate::Zero::new($zero);
        $crate::intern::InternTable::with_zero(&ZERO)
    }};
    (@ZERO($Name:ident, ())) => { };
    (@ZERO($Name:ident, ($v:ident, $zero:expr))) => {
        impl $Name {
            pub const $v: Self = $Name($crate::Zero::zero());
        }
    };
    (@DOIT, ($(#[$attr:meta])*), ($($vis:tt)*), $Name:ident, $T:ty,
     ( $($Lookup:ty)? ), ( $($Z:ident, $ze:expr)* ), ( $($serdes:tt)? ),
     ( $($S:ident)? ), ( $($M:ident)? ) ) => {
        intern_struct!{
            @SERDESDERIVE($($serdes)?);
            $(#[$attr])*
            #[derive(Copy, Clone, Eq, PartialEq, Hash)]
            #[repr(transparent)]
            $($vis)* struct $Name($crate::intern::Ref<$T>);
        }

        impl $crate::intern::InternId for $Name {
            type Intern = $T;
            intern_struct!(@LOOKUPTYPE($T, ($($Lookup)?)));

            #[inline]
            fn wrap(r: $crate::intern::Ref<$T>) -> Self {
                $Name(r)
            }

            #[inline]
            fn unwrap(self) -> $crate::intern::Ref<$T> {
                self.0
            }

            #[inline]
            fn table() -> &'static $crate::intern::InternTable<$Name, $T> {
                static TABLE: $crate::intern::InternTable<$Name, $T> =
                    intern_struct!(@TABLE($T, ($($Z, $ze)?)));
                &TABLE
            }
        }

        intern_struct!(@ZERO($Name, ($($Z, $ze)?)));

        intern_struct!(@BORROW($T, $Name, ($($Lookup)*)));

        intern_struct!(@SET(($($vis)*), $Name, ($($S)?)));

        intern_struct!(@MAP(($($vis)*), $Name, ($($M)?)));

        impl std::convert::From<$crate::intern::InternSerdes<$Name>> for $Name {
            #[inline]
            fn from(iid: $crate::intern::InternSerdes<$Name>) -> Self {
                iid.0
            }
        }

        impl std::ops::Deref for $Name {
            type Target = $T;
            #[inline]
            fn deref(&self) -> & $T {
                use $crate::intern::InternId;
                self.get()
            }
        }

        impl std::fmt::Debug for $Name {
            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
                use $crate::intern::InternId;
                self.get().fmt(f)
            }
        }
    };
}

#[cfg(test)]
mod tests {
    use serde_derive::Deserialize;
    use serde_derive::Serialize;

    use super::*;

    #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Deserialize, Serialize)]
    struct MyType {
        v: i64,
    }

    intern_struct! {
        struct MyId = Intern<MyType> {
            serdes("InternSerdes<MyId>");
            type Set = _MyIdSet;
            type Map = _MyIdMap;
        }
    }

    impl std::cmp::PartialOrd for MyId {
        fn partial_cmp(&self, other: &Self) -> std::option::Option<std::cmp::Ordering> {
            self.get().partial_cmp(other.get())
        }
    }

    impl std::cmp::Ord for MyId {
        fn cmp(&self, other: &Self) -> std::cmp::Ordering {
            self.get().cmp(other.get())
        }
    }

    #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Deserialize, Serialize)]
    pub struct PubType {
        v: i64,
    }

    intern_struct! {
        pub struct PubId = Intern<PubType> {
            serdes("InternSerdes<PubId>");
            type Set = _PubIdSet;
            type Map = _PubIdMap;
        }
    }

    #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Deserialize, Serialize)]
    pub(crate) struct CrateType {
        v: i64,
    }

    intern_struct! {
        pub(crate) struct CrateId = Intern<CrateType> {
            serdes("InternSerdes<CrateId>");
            type Set = _CrateIdSet;
            type Map = _CrateIdMap;
        }
    }

    #[test]
    fn test() {
        let m1 = MyType { v: 1 };
        let m2 = MyType { v: 1 };
        let m3 = MyType { v: -57 };
        let i1 = MyId::intern(m1);
        let i2 = MyId::intern(m2);
        let i3 = MyId::intern(m3);
        assert_eq!(i1, i2);
        assert_eq!(i1.get().v, 1);
        assert_ne!(i1, i3);
        assert_eq!(i3.v, -57); // Uses Deref
        assert!(i3 < i1);
    }

    #[test]
    fn pub_test() {
        let m1 = PubType { v: 1 };
        let m2 = PubType { v: 1 };
        let m3 = PubType { v: -57 };
        let i1 = PubId::intern(m1);
        let i2 = PubId::intern(m2);
        let i3 = PubId::intern(m3);
        assert_eq!(i1, i2);
        assert_eq!(i1.get().v, 1);
        assert_ne!(i1, i3);
        assert_eq!(i3.v, -57); // Uses Deref
    }

    #[test]
    fn crate_test() {
        let m1 = CrateType { v: 1 };
        let m2 = CrateType { v: 1 };
        let m3 = CrateType { v: -57 };
        let i1 = CrateId::intern(m1);
        let i2 = CrateId::intern(m2);
        let i3 = CrateId::intern(m3);
        assert_eq!(i1, i2);
        assert_eq!(i1.get().v, 1);
        assert_ne!(i1, i3);
        assert_eq!(i3.v, -57); // Uses Deref
    }

    #[test]
    fn test_bincode_works_with_serialize_into() {
        let m1 = CrateType { v: 1 };
        let m2 = CrateType { v: -57 };
        let i1 = CrateId::intern(m1);
        let i2 = CrateId::intern(m2);
        let val = (i1, i2, i1);
        let mut serialized: Vec<u8> = vec![];
        {
            let _guard = SerGuard::default();
            bincode::serialize_into(&mut serialized, &val).unwrap()
        }
        let deserialized: (CrateId, CrateId, CrateId) = {
            let _guard = DeGuard::default();
            bincode::deserialize(&serialized).unwrap()
        };
        assert_eq!(deserialized, val);
    }

    #[test]
    fn round_trip_bincode() {
        let m1 = CrateType { v: 1 };
        let m2 = CrateType { v: -57 };
        let i1 = CrateId::intern(m1);
        let i2 = CrateId::intern(m2);
        let val = (i1, i2, i1);
        let serialized: Vec<u8> = { bincode::serialize(&WithIntern(&val)).unwrap() };
        let deserialized: (CrateId, CrateId, CrateId) =
            { WithIntern::strip(bincode::deserialize(&serialized)).unwrap() };
        assert_eq!(deserialized, val);
    }

    #[test]
    fn round_trip_json() {
        use serde_json;
        let m1 = CrateType { v: 1 };
        let m2 = CrateType { v: -57 };
        let i1 = CrateId::intern(m1);
        let i2 = CrateId::intern(m2);
        let val = (i1, i2, i1);
        let serialized: String = { serde_json::to_string_pretty(&WithIntern(&val)).unwrap() };
        let deserialized: (CrateId, CrateId, CrateId) =
            { WithIntern::strip(serde_json::from_str(&serialized)).unwrap() };
        assert_eq!(deserialized, val);
    }
}

pub trait Lookup {
    fn lookup(self) -> &'static str;
}

#[macro_export]
macro_rules! impl_lookup {
    ($named:ident) => {
        impl Lookup for $named {
            fn lookup(self) -> &'static str {
                self.0.lookup()
            }
        }
    };
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

//! Compact, serializable, mostly-lock-free interning.
//!
//! A library for [interning](https://en.wikipedia.org/wiki/String_interning)
//! strings, byte slices, and other data in Rust.  Interned values are
//! deduplicated.  A reference to interned data can be represented in 32 bits,
//! so that frequently-used references take up half as much space on a system
//! with 64-bit pointers.
//!
//! # Why another intern crate?
//!
//! Compared to similar crates (such as
//! [internment](https://crates.io/crates/internment)), this crate offers the
//! following advantages:
//! * Compact 32-bit ids.  This crate was written to save space on internal
//!   program analysis data structures.  (If you don't need this, you can use
//!   `get()` to turn an `Id` into a `&'static T`.)
//! * Sharded locking when interning new objects.  We use
//!   [rayon](https://docs.rs/rayon/1.5.1/rayon/) in our code and need to be
//!   able to intern data in parallel.
//! * Lock-free constant-time `deref()` of intern ids.  Again, we need to be
//!   able read interned data from numerous threads.  Compared to using a
//!   pointer, this requires a single extra load and no extra storage beyond
//!   the fixed-size part of the arena.
//! * Support for deduplicating serialization and deserialization using
//!   [serde](https://serde.rs/).  This allows us to preserve sharing when
//!   doing I/O, and to automatically re-intern data when it is read.
//! * Support for fast hashing using the `IdHasher` type.
//!
//! # Data is leaked
//!
//! There is one important downside to this crate that is shared by some, but
//! not all, intern crates: interned data is leaked and will not be reclaimed
//! until the program terminates.  If that's a problem you may need to consider
//! a scoped interning crate (but note that efficient threading support may be
//! hard to find).
//!
//! # Using string interning
//!
//! Simply import `intern::string` and go to town:
//! ```
//! use intern::string::{self, StringId};
//!
//! let a: StringId = string::intern("a");
//! let b = string::intern("b");
//! let a_again: Option<StringId> = Some(string::intern("a")); // still 32 bits
//! assert_eq!(Some(a), a_again);
//! assert_eq!(format!("{} {} {}", a, b, a_again.unwrap()), "a b a");
//! ```
//!
//! # How to define an interned type
//!
//! Say we want to intern `MyType`.  Simply use the
//! [`intern_struct!`](intern_struct) macro as shown below.  Now
//! `MyId::intern(my_type)` will intern an object and `&*my_id` will retrieve a
//! static reference to the interned object referred to by `my_id`.
//! ```
//! # #[macro_use]
//! use intern::{InternId, InternSerdes, intern_struct};
//! use serde_derive::{Deserialize, Serialize};
//!
//! #[derive(Debug, PartialEq, Eq, Hash, Deserialize, Serialize)]
//! struct MyType{ v: i64 }
//!
//! intern_struct! {
//!     struct MyId = Intern<MyType> { serdes("InternSerdes<MyId>"); }
//! }
//!
//! # fn main() {
//! let m1 = MyType{ v: 1 };
//! let m2 = MyType{ v: 1 };
//! let m3 = MyType{ v: -57 };
//! let i1 = MyId::intern(m1);
//! let i2 = MyId::intern(m2);
//! let i3 = MyId::intern(m3);
//! assert_eq!(i1, i2);
//! assert_eq!(i1.get().v, 1);
//! assert!(i1 != i3);
//! assert_eq!(i3.v, -57);  // Uses Deref
//! # }
//! ```
//!
//! # Using InternId serde support
//!
//! `InternId`s defined with a `serdes` clause as shown above support
//! de-duplication of common ids during serialization.  This can be
//! especially useful for interned strings, but is applicable to any
//! intern type whose target is serializable.  Because this
//! deduplication is stateful but the serde api is stateless, you'll
//! need to wrap the entire data structure you serialize in
//! `WithIntern` during serialization and deserialization:
//! ```
//! # use bincode::Result;
//! # use crate::intern::WithIntern;
//! # type MyId = crate::intern::string::StringId;
//! pub fn serialize(v: &[MyId]) -> Result<Vec<u8>> {
//!     bincode::serialize(&WithIntern(v))
//! }
//!
//! pub fn deserialize(encoded: &[u8]) -> Result<Vec<MyId>> {
//!     WithIntern::strip(bincode::deserialize(encoded))
//! }
//! ```
//! Note in particular that dropping a context resets sharing; if you create
//! and drop a context during serialization, you must create and drop it at the
//! same point during deserialization and vice versa.

mod atomic_arena;
#[doc(hidden)]
pub mod idhasher;
pub mod intern;
pub mod path;
mod sharded_set;
mod small_bytes;
pub mod string;
pub mod string_key;
#[doc(hidden)]
pub use crate::atomic_arena::Zero;
#[doc(inline)]
pub use crate::idhasher::BuildIdHasher;
#[doc(inline)]
pub use crate::idhasher::IdHasher;
#[doc(inline)]
pub use crate::intern::AsInterned;
#[doc(inline)]
pub use crate::intern::DeGuard;
#[doc(inline)]
pub use crate::intern::InternId;
#[doc(inline)]
pub use crate::intern::InternSerdes;
pub use crate::intern::Lookup;
#[doc(inline)]
pub use crate::intern::SerGuard;
#[doc(inline)]
pub use crate::intern::WithIntern;


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

use std::cmp::Ordering;
use std::ffi::OsStr;
#[cfg(unix)]
use std::os::unix::ffi::OsStrExt;
use std::path::Path;
use std::path::PathBuf;

use serde_derive::Deserialize;
use serde_derive::Serialize;

use crate::idhasher::BuildIdHasher;
use crate::intern_struct;
use crate::string;
use crate::InternId;
use crate::InternSerdes;

intern_struct! {
    /// A path whose path components are interned.
    pub struct PathId = Intern<PathNode> {
        serdes("InternSerdes<PathId>");
        type Set = PathIdSet;
        type Map = PathIdMap;
    }
}

pub type PathIdIndexMap<V> = indexmap::IndexMap<PathId, V, BuildIdHasher<u32>>;
pub type PathIdIndexSet = indexmap::IndexSet<PathId, BuildIdHasher<u32>>;

impl PathId {
    /// Intern the given path one `std::path::Component` at a time using the
    /// `Path::components()` iterator, which normalizes during parsing.  Panic
    /// on empty paths such as `/`.
    #[cfg(unix)]
    pub fn intern<P: AsRef<Path>>(mut parent: Option<PathId>, path: P) -> Self {
        for c in path.as_ref().iter() {
            let p = InternId::intern(PathNode {
                name: string::intern_bytes(c.as_bytes()),
                parent,
            });
            parent = Some(p);
        }
        parent.unwrap()
    }

    /// Intern the given path one `std::path::Component` at a time using the
    /// `Path::components()` iterator, which normalizes during parsing.  Panic
    /// on empty paths such as `/`.
    #[cfg(not(unix))]
    pub fn intern<P: AsRef<Path>>(mut parent: Option<PathId>, path: P) -> Self {
        for c in path.as_ref().iter() {
            let p = InternId::intern(PathNode {
                name: string::intern(c.to_str().unwrap()),
                parent,
            });
            parent = Some(p);
        }
        parent.unwrap()
    }

    pub fn parent(&self) -> Option<PathId> {
        self.parent
    }

    /// Returns the final component as an &OsStr.
    pub fn file_name(&self) -> &OsStr {
        self.get().file_name()
    }

    /// Linearize this path as a PathBuf.
    pub fn to_path_buf(&self) -> PathBuf {
        self.get().to_path_buf()
    }

    /// Linearize this path, appending to an existing PathBuf.
    pub fn push_to(&self, buf: &mut PathBuf) {
        self.get().push_to(buf);
    }
}

impl<P: AsRef<Path>> From<P> for PathId {
    fn from(path: P) -> Self {
        Self::intern(None, path)
    }
}

impl From<PathId> for PathBuf {
    fn from(id: PathId) -> Self {
        id.to_path_buf()
    }
}

impl std::fmt::Display for PathId {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        self.to_path_buf().display().fmt(f)
    }
}

impl std::cmp::Ord for PathId {
    /// Compare the linearized components of self to other in top-down order.
    fn cmp(&self, other: &Self) -> Ordering {
        type LinearPath = smallvec::SmallVec<[PathComponentId; 20]>;
        fn linearize(mut p: PathId) -> LinearPath {
            let mut v: LinearPath = Default::default();
            v.push(p.name);
            while let Some(parent) = p.parent {
                v.push(parent.name);
                p = parent;
            }
            v
        }
        linearize(*self)
            .iter()
            .rev()
            .cmp(linearize(*other).iter().rev())
    }
}

impl std::cmp::PartialOrd for PathId {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

#[cfg(unix)]
type PathComponentId = string::BytesId;

#[cfg(not(unix))]
type PathComponentId = string::StringId;

/// A `PathNode` represents a `Path` as a list, starting with a leaf component
/// and walking up the directory tree.  This is really an internal detail, but
/// is exposed by `PathId::Intern`.
#[doc(hidden)]
#[derive(Hash, PartialEq, Eq, Clone, Copy, Serialize, Deserialize)]
pub struct PathNode {
    name: PathComponentId,
    parent: Option<PathId>,
}

impl PathNode {
    /// Returns the final component as an &OsStr.
    #[cfg(unix)]
    fn file_name(&self) -> &OsStr {
        OsStr::from_bytes(self.name.as_bytes())
    }

    /// Returns the final component as an &OsStr.
    #[cfg(not(unix))]
    fn file_name(&self) -> &OsStr {
        OsStr::new(self.name.as_str())
    }

    /// Linearize this path as a PathBuf.
    fn to_path_buf(&self) -> PathBuf {
        let mut path = match self.parent {
            Some(parent) => parent.to_path_buf(),
            None => PathBuf::new(),
        };
        path.push(self.file_name());
        path
    }

    /// Linearize this path, appending to an existing PathBuf.
    fn push_to(&self, buf: &mut PathBuf) {
        if let Some(parent) = self.parent {
            parent.push_to(buf);
        }
        buf.push(self.file_name());
    }
}

impl std::fmt::Debug for PathNode {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        self.to_path_buf().fmt(f)
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_eq() {
        assert_eq!(PathId::from("a"), PathId::from("a/"));
        assert_ne!(PathId::from("a"), PathId::from("/a"));
        assert_eq!(PathId::from("a/b"), PathId::from("a//b"));
    }

    #[test]
    fn test_cmp() {
        assert_eq!(PathId::from("a").cmp(&PathId::from("a")), Ordering::Equal);
        assert_eq!(PathId::from("a").cmp(&PathId::from("b")), Ordering::Less);
        assert_eq!(PathId::from("a").cmp(&PathId::from("a/a")), Ordering::Less);
        assert_eq!(PathId::from("a").cmp(&PathId::from("a/_")), Ordering::Less);
        assert_eq!(PathId::from("b").cmp(&PathId::from("a")), Ordering::Greater);
        assert_eq!(
            PathId::from("a/a").cmp(&PathId::from("a")),
            Ordering::Greater
        );
        assert_eq!(
            PathId::from("a/_").cmp(&PathId::from("a")),
            Ordering::Greater
        );
        assert_eq!(
            PathId::from("a/b").cmp(&PathId::from("a.b")),
            Ordering::Less
        );
    }

    #[test]
    fn test_intern() {
        assert_eq!(PathId::from("a/b"), PathId::intern(None, "a/b"));
        assert_eq!(
            PathId::from("a/b/c"),
            PathId::intern(Some(PathId::from("a")), "b/c"),
        );
    }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

use std::borrow::Borrow;
use std::collections::hash_map::RandomState;
use std::fmt;
use std::hash::BuildHasher;
use std::hash::Hash;
use std::hash::Hasher;

use hashbrown::raw::RawTable;
use parking_lot::RwLock;
use parking_lot::RwLockWriteGuard;

const SHARD_SHIFT: usize = 6;
const SHARDS: usize = 1 << SHARD_SHIFT;

pub struct ShardedSet<T, S = RandomState> {
    build_hasher: S,
    shards: [RwLock<RawTable<T>>; SHARDS],
}

impl<T, S> fmt::Debug for ShardedSet<T, S> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "ShardedSet")
    }
}

impl<T, S: BuildHasher + Default> Default for ShardedSet<T, S> {
    fn default() -> Self {
        Self::with_hasher(Default::default())
    }
}

impl<T, S: BuildHasher> ShardedSet<T, S> {
    pub fn with_hasher(h: S) -> Self {
        Self {
            build_hasher: h,
            shards: [
                Default::default(),
                Default::default(),
                Default::default(),
                Default::default(),
                Default::default(),
                Default::default(),
                Default::default(),
                Default::default(),
                Default::default(),
                Default::default(),
                Default::default(),
                Default::default(),
                Default::default(),
                Default::default(),
                Default::default(),
                Default::default(),
                Default::default(),
                Default::default(),
                Default::default(),
                Default::default(),
                Default::default(),
                Default::default(),
                Default::default(),
                Default::default(),
                Default::default(),
                Default::default(),
                Default::default(),
                Default::default(),
                Default::default(),
                Default::default(),
                Default::default(),
                Default::default(),
                Default::default(),
                Default::default(),
                Default::default(),
                Default::default(),
                Default::default(),
                Default::default(),
                Default::default(),
                Default::default(),
                Default::default(),
                Default::default(),
                Default::default(),
                Default::default(),
                Default::default(),
                Default::default(),
                Default::default(),
                Default::default(),
                Default::default(),
                Default::default(),
                Default::default(),
                Default::default(),
                Default::default(),
                Default::default(),
                Default::default(),
                Default::default(),
                Default::default(),
                Default::default(),
                Default::default(),
                Default::default(),
                Default::default(),
                Default::default(),
                Default::default(),
                Default::default(),
            ],
        }
    }
}

fn hash_one<B: BuildHasher, T: Hash>(build_hasher: &B, x: T) -> u64 {
    let mut hasher = build_hasher.build_hasher();
    x.hash(&mut hasher);
    hasher.finish()
}

pub struct InsertLock<'a, T, S = RandomState> {
    build_hasher: &'a S,
    hash: u64,
    shard: RwLockWriteGuard<'a, RawTable<T>>,
}

impl<'a, T, S> fmt::Debug for InsertLock<'a, T, S> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "InsertLock(hash: {:x})", self.hash)
    }
}

impl<T: Eq + Hash, S: BuildHasher> ShardedSet<T, S> {
    /// Return the hash, the shard index corresponding to the hash.  Since
    /// hashbrown uses the upper 7 bits for disambiguation and the lower bits
    /// for bucket indexing, we take the bits just above the top 7.
    #[inline(always)]
    fn hash_and_shard<Q>(&self, q: &Q) -> (u64, &RwLock<RawTable<T>>)
    where
        T: Borrow<Q>,
        Q: ?Sized + Hash,
    {
        let hash = hash_one(&self.build_hasher, q);
        (
            hash,
            &self.shards[(hash >> (64 - 7 - SHARD_SHIFT)) as usize & (SHARDS - 1)],
        )
    }

    /// Clone out the entry corresponding to `q` if it exists, otherwise return
    /// `Err(InsertLock)` so that it can subsequently be inserted.
    pub fn get_or_insert_lock<'a, Q>(&'a self, q: &Q) -> Result<T, InsertLock<'a, T, S>>
    where
        T: Borrow<Q> + Clone,
        Q: ?Sized + Hash + Eq,
    {
        let (hash, shard) = self.hash_and_shard(q);
        // Assume load is low and try to take lock for writing.
        // We don't faff around with upgradability right now.
        let shard = if let Some(write_lock) = shard.try_write() {
            write_lock
        } else {
            // Write contention.  Try reading first to see if the entry already exists.
            if let Some(t) = shard.read().get(hash, |other| q == other.borrow()) {
                // Already exists.
                return Ok(t.clone());
            }
            // Unconditionally write lock.
            shard.write()
        };
        // Now check for the data.  We need to do this even if we already
        // checked in the write contention case above.  We don't use an
        // upgradable read lock because those are exclusive from one another
        // just like write locks.
        if let Some(t) = shard.get(hash, |other| q == other.borrow()) {
            return Ok(t.clone());
        }
        Err(InsertLock {
            build_hasher: &self.build_hasher,
            hash,
            shard,
        })
    }

    /// Clone out the entry corresponding to `q` if it exists.
    pub fn get<Q>(&self, q: &Q) -> Option<T>
    where
        T: Borrow<Q> + Clone,
        Q: ?Sized + Hash + Eq,
    {
        let (hash, shard) = self.hash_and_shard(q);
        shard
            .read()
            .get(hash, |other| q == other.borrow())
            .map(Clone::clone)
    }

    /// Unconditionally insert `t` without checking if it's in the set.
    pub fn unchecked_insert(&self, t: T) {
        let build_hasher = &self.build_hasher;
        let (hash, shard) = self.hash_and_shard(&t);
        shard.write().insert(hash, t, |v| hash_one(build_hasher, v));
    }
}

impl<T: Sized + Hash, S: BuildHasher> InsertLock<'_, T, S> {
    /// Insert the given value into the set.  This value must borrow-match the
    /// original value from get_or_insert_lock.
    pub fn insert<Q: Into<T>>(&mut self, q: Q) {
        let build_hasher = self.build_hasher;
        self.shard
            .insert(self.hash, q.into(), |v| hash_one(build_hasher, v));
    }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

use std::ascii::escape_default;
use std::borrow::Borrow;
use std::fmt::Debug;
use std::fmt::Formatter;
use std::fmt::Result;
use std::hash::Hash;
use std::hash::Hasher;
use std::mem::size_of;
use std::ops::Deref;

use serde_derive::Deserialize;
use serde_derive::Serialize;

const SMALL_MAX_LEN: usize = 3 * size_of::<usize>() - 2;

/// A SmallBytes is 3 pointer-sized words (the same size as a vec);
/// one byte is used for the enum tag and one is used for the length so
/// the longest string that can be stored inline is 2 bytes shorter
/// than that.
#[derive(Eq, Clone, Deserialize, Serialize)]
#[serde(from = "serde_bytes::ByteBuf")]
#[serde(into = "serde_bytes::ByteBuf")]
pub enum SmallBytes {
    Small { len: u8, bytes: [u8; SMALL_MAX_LEN] },
    Large(Box<[u8]>),
}

use SmallBytes::*;

impl SmallBytes {
    pub const fn empty() -> SmallBytes {
        Small {
            len: 0,
            bytes: [0; SMALL_MAX_LEN],
        }
    }

    pub fn len(&self) -> usize {
        match self {
            Small { len, .. } => *len as usize,
            Large(b) => b.len(),
        }
    }

    pub fn is_empty(&self) -> bool {
        self.len() == 0
    }
}

impl Debug for SmallBytes {
    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
        let s: String =
            String::from_utf8(self.iter().map(|b| escape_default(*b)).flatten().collect()).unwrap();
        match self {
            Small { len, .. } => write!(f, "Small{{len:{},bytes:b\"{}\"}}", *len, s),
            Large(_) => write!(f, "Large(b\"{}\")", s),
        }
    }
}

// If a slice is small enough to make a Small, we need to copy across
// the bytes we need.  Don't attempt to take ownership of the existing
// value.
fn make_small(b: &[u8]) -> Option<SmallBytes> {
    let l = b.len();
    if l <= SMALL_MAX_LEN {
        let mut bytes = [0; SMALL_MAX_LEN];
        bytes[0..l].copy_from_slice(b);
        Some(Small {
            len: l as u8,
            bytes,
        })
    } else {
        None
    }
}

impl Deref for SmallBytes {
    type Target = [u8];
    fn deref(&self) -> &[u8] {
        match self {
            Small { len, bytes } => &bytes[0..*len as usize],
            Large(b) => b,
        }
    }
}

impl AsRef<[u8]> for SmallBytes {
    fn as_ref(&self) -> &[u8] {
        self.deref()
    }
}

impl From<&[u8]> for SmallBytes {
    fn from(u: &[u8]) -> SmallBytes {
        if let Some(r) = make_small(u) {
            r
        } else {
            Large(u.into())
        }
    }
}

impl From<Box<[u8]>> for SmallBytes {
    fn from(u: Box<[u8]>) -> SmallBytes {
        if let Some(r) = make_small(&*u) {
            r
        } else {
            Large(u)
        }
    }
}

impl From<Vec<u8>> for SmallBytes {
    fn from(u: Vec<u8>) -> SmallBytes {
        if let Some(r) = make_small(&*u) {
            r
        } else {
            Large(u.into())
        }
    }
}

impl From<&str> for SmallBytes {
    #[inline]
    fn from(u: &str) -> SmallBytes {
        str::as_bytes(u).into()
    }
}

impl From<Box<str>> for SmallBytes {
    #[inline]
    fn from(u: Box<str>) -> SmallBytes {
        u.into_boxed_bytes().into()
    }
}

impl From<String> for SmallBytes {
    #[inline]
    fn from(u: String) -> SmallBytes {
        u.into_bytes().into()
    }
}

impl Borrow<[u8]> for SmallBytes {
    #[inline]
    fn borrow(&self) -> &[u8] {
        self.deref()
    }
}

impl From<serde_bytes::ByteBuf> for SmallBytes {
    fn from(bb: serde_bytes::ByteBuf) -> Self {
        bb.into_vec().into() // into_vec is just an unwrap.
    }
}

impl From<SmallBytes> for serde_bytes::ByteBuf {
    fn from(sb: SmallBytes) -> Self {
        let v: Vec<u8> = match sb {
            Small { len, bytes } => (&bytes[0..len as usize]).into(),
            Large(b) => b.into(),
        };
        serde_bytes::ByteBuf::from(v)
    }
}

impl PartialEq for SmallBytes {
    fn eq(&self, other: &Self) -> bool {
        self.deref() == other.deref()
    }
}

impl Hash for SmallBytes {
    fn hash<H: Hasher>(&self, state: &mut H) {
        self.deref().hash(state)
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    // Make sure we can use empty() in a const context.
    const EMPTY: SmallBytes = SmallBytes::empty();

    fn hash<H: Hash>(h: H) -> u64 {
        use std::collections::hash_map::DefaultHasher;
        let mut hasher = DefaultHasher::new();
        h.hash(&mut hasher);
        hasher.finish()
    }

    #[test]
    fn empty() {
        assert_eq!(3 * size_of::<usize>(), size_of::<SmallBytes>());
        assert_eq!(EMPTY, SmallBytes::empty());
        let e: &'static [u8] = b"";
        let l = SmallBytes::from(e);
        assert_eq!(l, EMPTY);
        assert_eq!(&*l, e);
        assert_eq!(&*EMPTY, e);
        assert_eq!(hash(&l), hash(e));
        let v: Vec<u8> = Vec::new();
        let ll = SmallBytes::from(v); // Consumes v.
        let ls: &[u8] = &*ll;
        assert_eq!(ll, l);
        assert_eq!(ls, b"");
        let vs: &[u8] = &Vec::new();
        assert_eq!(ls, vs);
        assert_eq!(hash(ll), hash(e));
        // Check that all zeros is empty, and fail compile if sizes are wrong.
        let zeros = [0usize; 3];
        let lz: SmallBytes = unsafe { std::mem::transmute(zeros) };
        assert_eq!(EMPTY, lz);
    }

    #[test]
    fn small() {
        let h: &'static [u8] = b"hello";
        let hi: &'static [u8] = b"hi";
        let l = SmallBytes::from(h);
        let li = SmallBytes::from(hi);
        assert_eq!(&*l, h);
        assert_eq!(hi, &*li);
        assert!(l != EMPTY);
        assert!(li != EMPTY);
        assert!(l != li);
        assert!(&*l != hi);
        assert!(hi != &*l);
        assert_eq!(hash(&l), hash(h));
        assert_eq!(hash(&li), hash(hi));
        let mut v = Vec::new();
        v.extend(b"hello");
        let ll = SmallBytes::from(v);
        assert_eq!(l, ll);
        assert_eq!(h, &*ll);
        assert_eq!(hash(&ll), hash(&l));
    }

    #[test]
    fn large() {
        #![allow(clippy::op_ref)]
        let abc: &'static [u8] = b"Jackdaws love my big pink sphinx of quartz.";
        let fox: &'static [u8] = b"The quick brown fox jumps over the lazy dg.";
        let labc = SmallBytes::from(abc);
        let lfox = SmallBytes::from(fox);
        assert_eq!(&*labc, abc);
        assert_eq!(fox, &*lfox);
        assert!(labc != lfox);
        assert!(&labc != &lfox);
        assert!(&*labc != &*lfox);
        assert_eq!(hash(abc), hash(&labc));
        assert_eq!(hash(fox), hash(&lfox));
        let mut v = Vec::new();
        v.extend(abc);
        let lv = SmallBytes::from(v);
        let mut w: Vec<u8> = Vec::new();
        w.extend(abc);
        assert_eq!(&*lv, &w[..]);
        assert_eq!(&*lv, abc);
        assert_eq!(lv, labc);
        assert_eq!(hash(&lv), hash(labc));
    }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

use std::borrow::Borrow;
use std::borrow::Cow;
use std::cmp::Ordering;
use std::collections::HashMap;
use std::collections::HashSet;
use std::fmt;
use std::fmt::Formatter;
use std::hash::Hash;
use std::str::FromStr;
use std::str::Utf8Error;

#[doc(hidden)]
pub use once_cell::sync::Lazy; // For macros
use serde_derive::Deserialize;
use serde_derive::Serialize;

use crate::idhasher::BuildIdHasher;
use crate::intern::InternId;
use crate::intern::InternSerdes;
use crate::intern_struct;
use crate::small_bytes::SmallBytes;

intern_struct! {
    /// An opaque token corresponding to an interned &[u8].
    pub struct BytesId = Intern<SmallBytes> {
        serdes("InternSerdes<BytesId>");
        type Lookup = [u8];
        const EMPTY = SmallBytes::empty();
    }
}

impl BytesId {
    /// Recover the original interned bytes.
    #[inline]
    pub fn as_bytes(self) -> &'static [u8] {
        // Safe because BytesId can only be generated
        // by a call to intern, which returns the result
        // of id_to_bytes.push.
        &*self.get()
    }
}

impl PartialOrd for BytesId {
    fn partial_cmp(&self, other: &BytesId) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

impl Ord for BytesId {
    fn cmp(&self, other: &BytesId) -> Ordering {
        if self == other {
            Ordering::Equal
        } else {
            self.get().cmp(other.get())
        }
    }
}

pub type BytesIdMap<V> = HashMap<BytesId, V, BuildIdHasher<u32>>;
pub type BytesIdSet = HashSet<BytesId, BuildIdHasher<u32>>;

/// An opaque token corresponding to an interned &str.
///
/// You can recover the str with id.as_str() or using format!.
#[derive(Copy, Clone, Hash, Eq, PartialEq)]
#[derive(Serialize, Deserialize)]
#[repr(transparent)]
pub struct StringId(BytesId);

impl StringId {
    /// We always pre-reserve a blank entry.
    pub const EMPTY: StringId = StringId(BytesId::EMPTY);

    /// Convert from raw bytes, which can only succeed if the bytes are valid utf-8.
    pub fn from_bytes(bytes: BytesId) -> Result<StringId, Utf8Error> {
        match std::str::from_utf8(bytes.as_bytes()) {
            Ok(_) => Ok(StringId(bytes)),
            Err(e) => Err(e),
        }
    }

    /// Convert to a static string.
    pub fn as_str(self) -> &'static str {
        // This is actually safe because the bytes we are converting originally came
        // from a str when we interned it. So they must be well-formed UTF8.
        unsafe { std::str::from_utf8_unchecked(self.0.as_bytes()) }
    }

    /// Intern index for the underlying bytes.
    pub fn index(self) -> u32 {
        (self.0).0.index() as u32
    }

    pub fn from_index_checked(index: u32) -> Option<Self> {
        BytesId::from_index_checked(index).map(Self)
    }

    pub unsafe fn from_index(index: u32) -> Self {
        Self(BytesId::from_index(index))
    }

    /// 0-cost conversion to interned bytes.
    pub fn as_bytes(self) -> BytesId {
        self.0
    }

    pub fn is_empty(self) -> bool {
        self == Self::EMPTY
    }
}

impl fmt::Display for StringId {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        f.write_str(self.as_str())
    }
}

impl fmt::Debug for StringId {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        f.write_str(self.as_str())
    }
}

impl PartialOrd for StringId {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

impl Ord for StringId {
    fn cmp(&self, other: &Self) -> Ordering {
        self.as_str().cmp(other.as_str())
    }
}

// Describes types that can be viewed as a `[u8]`.
pub trait IntoUtf8Bytes: Sized {
    type Bytes: Into<SmallBytes> + Borrow<[u8]>;
    /// Convert into utf-8 encoded bytes while preserving the
    /// ownedness of the underlying storage if possible.
    fn into_bytes(self) -> Self::Bytes;
}

impl<'a> IntoUtf8Bytes for &'a str {
    type Bytes = &'a [u8];
    fn into_bytes(self) -> &'a [u8] {
        self.as_ref()
    }
}

impl IntoUtf8Bytes for Box<str> {
    type Bytes = Box<[u8]>;
    fn into_bytes(self) -> Box<[u8]> {
        From::from(self)
    }
}

impl<'a> IntoUtf8Bytes for Cow<'a, str> {
    type Bytes = Vec<u8>;
    fn into_bytes(self) -> Self::Bytes {
        self.into_owned().into_bytes()
    }
}

impl IntoUtf8Bytes for String {
    type Bytes = Vec<u8>;
    fn into_bytes(self) -> Vec<u8> {
        From::from(self)
    }
}

impl<'a> IntoUtf8Bytes for &'a String {
    type Bytes = &'a [u8];
    fn into_bytes(self) -> &'a [u8] {
        self.as_ref()
    }
}

impl FromStr for StringId {
    type Err = std::convert::Infallible;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        Ok(intern(s))
    }
}

pub type StringIdMap<V> = HashMap<StringId, V, BuildIdHasher<u32>>;
pub type StringIdSet = HashSet<StringId, BuildIdHasher<u32>>;

// A copy-on-write object that can be an interned string, a reference to a
// string, or an owned but uninterned string.
#[derive(Debug)]
pub enum CowStringId<'a> {
    Id(StringId),
    Ref(&'a str),
    Owned(String),
}

impl CowStringId<'_> {
    pub fn as_bytes(&self) -> &[u8] {
        self.as_str().as_bytes()
    }

    pub fn as_str(&self) -> &str {
        match self {
            CowStringId::Id(s) => s.as_str(),
            CowStringId::Ref(s) => s,
            CowStringId::Owned(s) => s,
        }
    }

    pub fn into_owned(self) -> String {
        match self {
            CowStringId::Id(s) => s.as_str().to_owned(),
            CowStringId::Ref(s) => s.to_owned(),
            CowStringId::Owned(s) => s,
        }
    }

    pub fn to_string_id(&self) -> StringId {
        match self {
            CowStringId::Id(s) => *s,
            CowStringId::Ref(s) => intern(*s),
            CowStringId::Owned(s) => intern(s.as_str()),
        }
    }

    pub fn to_bytes_id(&self) -> BytesId {
        self.to_string_id().as_bytes()
    }
}

impl fmt::Display for CowStringId<'_> {
    fn fmt(&self, fmt: &mut Formatter<'_>) -> std::fmt::Result {
        self.as_str().fmt(fmt)
    }
}

impl From<StringId> for CowStringId<'_> {
    fn from(id: StringId) -> Self {
        CowStringId::Id(id)
    }
}

impl<'a> From<&'a str> for CowStringId<'a> {
    fn from(s: &'a str) -> CowStringId<'a> {
        CowStringId::Ref(s)
    }
}

impl<'a> From<String> for CowStringId<'a> {
    fn from(s: String) -> Self {
        CowStringId::Owned(s)
    }
}

pub fn intern<S: IntoUtf8Bytes>(s: S) -> StringId {
    StringId(intern_bytes(s.into_bytes()))
}

#[inline]
pub fn intern_bytes<S>(s: S) -> BytesId
where
    S: Into<SmallBytes> + Borrow<[u8]>,
{
    let b: SmallBytes = s.into();
    BytesId::intern(b)
}

/// Statically declare an interned string.
#[macro_export]
macro_rules! string_id {
    ($value:literal) => {{
        static INSTANCE: $crate::string::Lazy<$crate::string::StringId> =
            $crate::string::Lazy::new(|| $crate::string::intern($value));
        *INSTANCE
    }};
    ($_:expr) => {
        compile_error!("string_id! macro can only be used with string literals.")
    };
}

/// Statically declare some interned bytes.
#[macro_export]
macro_rules! bytes_id {
    ($value:literal) => {{
        static INSTANCE: $crate::string::Lazy<$crate::string::BytesId> =
            $crate::string::Lazy::new(|| $crate::string::intern_bytes($value as &[u8]));
        *INSTANCE
    }};
    ($_:expr) => {
        compile_error!("bytes_id! macro can only be used with literals.")
    };
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn simple_bytes() {
        // Test EMPTY first to catch race with pool init.
        assert_eq!(BytesId::EMPTY.as_bytes(), b"");
        let e = intern_bytes(&b""[..]);
        assert_eq!(BytesId::EMPTY, e);
        let ek: &'static [u8] = b"";
        let ee = intern_bytes(ek);
        assert_eq!(BytesId::EMPTY, ee);
        let a = intern_bytes(&b"this is interned bytes"[..]);
        let b = intern("this is interned bytes").as_bytes();
        let c = intern_bytes(&b"this is different"[..]);
        assert_eq!(a, b);
        assert_ne!(a, c);
        assert_eq!(a.as_bytes(), b"this is interned bytes");
        assert_eq!(c.as_bytes(), b"this is different");
    }

    #[test]
    fn simple() {
        let a = intern("this is an interned string");
        let b = intern("this is an interned string".to_string());
        let c = intern("this is different");
        assert_eq!(a, b);
        assert_ne!(a, c);
        assert_eq!(a.to_string(), "this is an interned string");
        assert_eq!(c.to_string(), "this is different");
        assert_eq!(StringId::EMPTY.to_string(), "");
    }

    fn test_interning(strs: Vec<String>) {
        // Make sure interning produces the same tokens.
        let ids1: Vec<StringId> = strs.iter().map(|s| intern(s.clone())).collect();
        let ids2: Vec<StringId> = strs.iter().map(|s| intern(s.as_ref() as &str)).collect();
        assert_eq!(ids1, ids2);

        // Make sure they map back to the original strings.
        let strs2: Vec<String> = ids1.iter().map(|sid| sid.to_string()).collect();
        assert_eq!(strs, strs2);
    }

    #[test]
    fn many() {
        let strs: Vec<String> = (0..3000).map(|n| format!("some {}", n)).collect();
        test_interning(strs)
    }

    #[test]
    fn big() {
        let long = format!("{:900}", "");
        let strs: Vec<String> = (0..20).map(|n| format!("{}{}", long, n)).collect();
        test_interning(strs);
    }

    #[test]
    fn serde() {
        use crate::intern::DeGuard;
        use crate::intern::SerGuard;
        let original = intern("hello world");
        let mut encoded = Vec::new();
        let g = SerGuard::default();
        bincode::serialize_into(&mut encoded, &original).unwrap();
        drop(g);
        assert!(encoded.len() > 11);
        let g = DeGuard::default();
        let decoded: StringId = bincode::deserialize(&encoded).unwrap();
        drop(g);
        assert_eq!(original, decoded);
    }

    #[test]
    fn multithreaded() {
        use std::sync::atomic::AtomicU32;
        use std::sync::atomic::Ordering;
        use std::sync::Arc;
        use std::thread;
        use std::u32;

        use rand::thread_rng;
        use rand::Rng;

        // Load test lots of threads creating strings, with load
        // gradually getting heavier on later (popular) strings.
        const N: usize = 20_000_000;
        const WRITERS: usize = 100;
        const MAX: usize = N / WRITERS;
        // Array to track index issued to each string.
        let mut avail: Arc<Vec<AtomicU32>> = Arc::new(Vec::with_capacity(MAX as usize));
        Arc::get_mut(&mut avail)
            .unwrap()
            .resize_with(N as usize, || AtomicU32::new(u32::MAX));
        let mut workers = Vec::new();
        for k in 0..WRITERS {
            let avail = avail.clone();
            workers.push(thread::spawn(move || {
                let mut rng = thread_rng();
                for i in 0..MAX {
                    let r = if k == 0 { i } else { rng.gen_range(i..MAX) };
                    let id = intern(r.to_string());
                    let ix = id.0.index();
                    let av = avail[r].load(Ordering::Relaxed);
                    if av == u32::MAX {
                        avail[r].store(ix, Ordering::Relaxed);
                    } else {
                        assert_eq!(av, ix);
                    }
                }
            }));
        }
        for w in workers {
            w.join().unwrap();
        }
    }

    #[test]
    fn all_kinds_of_bytes() {
        let d: &[u8] = b"Hello";
        let e: Vec<u8> = d.into();
        let f: Box<[u8]> = d.into();
        let di = intern_bytes(d);
        let ei = intern_bytes(e);
        let fi = intern_bytes(f);
        assert_eq!(di, ei);
        assert_eq!(di, fi);
    }

    #[test]
    fn all_kinds_of_strings() {
        let a: &str = "Hello";
        let b: String = a.into();
        let c: Box<str> = a.into();
        let ai = intern(a);
        let bi = intern(b);
        let ci = intern(c);
        assert_eq!(ai, bi);
        assert_eq!(ai, ci);
    }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

use std::collections::HashMap;
use std::collections::HashSet;
use std::fmt;
use std::fmt::Formatter;
use std::str::FromStr;

use indexmap::IndexMap;
use serde::Deserialize;
use serde::Deserializer;
use serde::Serialize;
use serde::Serializer;

use crate::idhasher::BuildIdHasher;
use crate::string;
use crate::string::IntoUtf8Bytes;
use crate::string::StringId;
pub use crate::Lookup;

// StringKey is a small impedence matcher around StringId.
// NOTE in particular that it does NOT do de-duplicating serde.
#[derive(Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd)]
#[repr(transparent)]
pub struct StringKey(StringId);

pub type StringKeyMap<V> = HashMap<StringKey, V, BuildIdHasher<u32>>;
pub type StringKeySet = HashSet<StringKey, BuildIdHasher<u32>>;
pub type StringKeyIndexMap<V> = IndexMap<StringKey, V, BuildIdHasher<u32>>;

pub trait Intern: IntoUtf8Bytes {
    fn intern(self) -> StringKey {
        StringKey(string::intern(self))
    }
}

impl<T: IntoUtf8Bytes> Intern for T {}

impl StringKey {
    pub fn index(self) -> u32 {
        self.0.index()
    }

    pub fn from_index_checked(index: u32) -> Option<Self> {
        StringId::from_index_checked(index).map(Self)
    }

    pub unsafe fn from_index(index: u32) -> Self {
        Self(StringId::from_index(index))
    }
}

impl Lookup for StringKey {
    fn lookup(self) -> &'static str {
        self.0.as_str()
    }
}

impl fmt::Display for StringKey {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        write!(f, "{}", self.lookup())
    }
}

impl fmt::Debug for StringKey {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        write!(f, "{:?}", self.lookup())
    }
}

impl Serialize for StringKey {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        serializer.serialize_str(self.lookup())
    }
}

impl<'de> Deserialize<'de> for StringKey {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        Deserialize::deserialize(deserializer).map(|s: String| s.intern())
    }
}

impl FromStr for StringKey {
    type Err = std::convert::Infallible;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        Ok(s.intern())
    }
}

#[macro_export]
macro_rules! intern {
    ($value:literal) => {{
        use $crate::string::Lazy;
        use $crate::string_key::Intern;
        static INSTANCE: Lazy<$crate::string_key::StringKey> = Lazy::new(|| $value.intern());
        *INSTANCE
    }};
    ($_:expr) => {
        compile_error!("intern! macro can only be used with string literals.")
    };
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

use core::cmp::Ordering;
use std::fmt;
use std::num::NonZeroU32;
use std::str::FromStr;
use std::sync::Arc;

use fnv::FnvHashMap;
use lazy_static::lazy_static;
use parking_lot::RwLock;
use serde::Deserialize;
use serde::Deserializer;
use serde::Serialize;
use serde::Serializer;

use crate::types::Intern;
use crate::types::RawInternKey;

/// Slices of bytes intern as BytesKey
impl Intern for &[u8] {
    type Key = BytesKey;

    fn intern(self) -> Self::Key {
        BytesKey(BYTES_TABLE.intern(self))
    }
}

/// Owned strings intern as StringKey, with the interning
/// based on the raw bytes of the string
impl Intern for String {
    type Key = StringKey;

    fn intern(self) -> Self::Key {
        StringKey(BYTES_TABLE.intern(self.as_bytes()))
    }
}

/// Str (slices) intern as StringKey, with the interning
/// based on the raw bytes of the str.
impl Intern for &str {
    type Key = StringKey;

    fn intern(self) -> Self::Key {
        StringKey(BYTES_TABLE.intern(self.as_bytes()))
    }
}

impl Intern for &String {
    type Key = StringKey;

    fn intern(self) -> Self::Key {
        StringKey(BYTES_TABLE.intern(self.as_bytes()))
    }
}

/// Interned bytes
#[derive(Copy, Clone, Eq, Ord, Hash, PartialEq, PartialOrd)]
pub struct BytesKey(RawInternKey);

impl BytesKey {
    pub fn lookup(self) -> &'static [u8] {
        BYTES_TABLE.lookup(self.0)
    }
}

impl fmt::Debug for BytesKey {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let bytes_value = self.lookup();
        write!(f, "{:?}", bytes_value)
    }
}

/// An interned string
#[derive(Copy, Clone, Eq, Hash, PartialEq)]
pub struct StringKey(RawInternKey);

impl Ord for StringKey {
    fn cmp(&self, other: &Self) -> Ordering {
        self.lookup().cmp(other.lookup())
    }
}

impl PartialOrd for StringKey {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        self.lookup().partial_cmp(other.lookup())
    }
}

impl FromStr for StringKey {
    type Err = std::convert::Infallible;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        Ok(s.intern())
    }
}

impl StringKey {
    /// Get a reference to the original str.
    pub fn lookup(self) -> &'static str {
        let bytes = BYTES_TABLE.lookup(self.0);
        // This is safe because the bytes we are converting originally came
        // from a str when we interned it: the only way to get a StringKey is
        // to intern an (already valid) string, so if we have a StringKey then
        // its bytes must be valid UTF-8.
        unsafe { std::str::from_utf8_unchecked(bytes) }
    }

    /// Convert the interned string key into an interned bytes key. Because
    /// strings intern as their raw bytes, this is an O(1) operation.
    /// Note the reverse (BytesKey.as_str) is a fallible operation since
    /// the bytes may not be valid UTF-8.
    pub fn as_bytes(self) -> BytesKey {
        BytesKey(self.0)
    }
}

impl fmt::Debug for StringKey {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let str_value = self.lookup();
        write!(f, "{:?}", str_value)
    }
}

impl fmt::Display for StringKey {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let str_value = self.lookup();
        write!(f, "{}", str_value)
    }
}

impl Serialize for StringKey {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        serializer.serialize_str(self.lookup())
    }
}

impl<'de> Deserialize<'de> for StringKey {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        Deserialize::deserialize(deserializer).map(|s: String| s.intern())
    }
}

// Static table used in the bytes/str Intern implementations
lazy_static! {
    static ref BYTES_TABLE: BytesTable = BytesTable::new();
}

/// Similar to the generic `InternTable` but customized for sequences of raw bytes (and strings).
pub struct BytesTable {
    data: Arc<RwLock<BytesTableData>>,
}

impl BytesTable {
    pub fn new() -> Self {
        Self {
            data: Arc::new(RwLock::new(BytesTableData::new())),
        }
    }

    pub fn intern(&self, value: &[u8]) -> RawInternKey {
        if let Some(prev) = self.data.read().get(value) {
            return prev;
        }
        let mut writer = self.data.write();
        writer.intern(value)
    }

    pub fn lookup(&self, key: RawInternKey) -> &'static [u8] {
        self.data.read().lookup(key)
    }
}

/// BytesTableData is similar to InternTableData but customized for sequences
/// of raw bytes (and notably, strings).
struct BytesTableData {
    // Raw data storage, allocated in large chunks
    buffer: Option<&'static mut [u8]>,
    // Reverse mapping of index=>value, used to convert an
    // interned key back to (a reference to) its value
    items: Vec<&'static [u8]>,
    // Mapping of values to their interned indices
    table: FnvHashMap<&'static [u8], RawInternKey>,
}

impl BytesTableData {
    const BUFFER_SIZE: usize = 4096;

    pub fn new() -> Self {
        Self {
            buffer: Some(Self::new_buffer()),
            items: vec![
                // Add buffer value so the used index starts at 1
                // and we can use a NonZero type.
                b"<sentinel>",
            ],
            table: Default::default(),
        }
    }

    fn new_buffer() -> &'static mut [u8] {
        Box::leak(Box::new([0; Self::BUFFER_SIZE]))
    }

    pub fn get(&self, value: &[u8]) -> Option<RawInternKey> {
        self.table.get(value).cloned()
    }

    // Copy the byte slice into 'static memory by appending it to a buffer, if there is room.
    // If the buffer fills up and the value is small, start over with a new buffer.
    // If the value is large, just give it its own memory.
    fn alloc(&mut self, value: &[u8]) -> &'static [u8] {
        let len = value.len();

        let mut buffer = self.buffer.take().unwrap();
        if len > buffer.len() {
            if len >= Self::BUFFER_SIZE / 16 {
                // This byte slice is so big it can just have its own memory.
                self.buffer = Some(buffer);
                return Box::leak(value.into());
            } else {
                buffer = Self::new_buffer()
            }
        }

        let (mem, remaining) = buffer.split_at_mut(len);
        mem.copy_from_slice(value);
        self.buffer = Some(remaining);

        mem
    }

    pub fn intern(&mut self, value: &[u8]) -> RawInternKey {
        // If there's an existing value return it
        if let Some(prev) = self.get(value) {
            return prev;
        }

        // Otherwise intern
        let key = RawInternKey::new(unsafe {
            // Safe because we initialize `self.items` with a sentinel value
            NonZeroU32::new_unchecked(self.items.len() as u32)
        });
        let static_bytes = self.alloc(value);
        self.items.push(static_bytes);
        self.table.insert(static_bytes, key);

        key
    }

    pub fn lookup(&self, key: RawInternKey) -> &'static [u8] {
        let index = key.as_usize();
        self.items[index]
    }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

use std::hash::Hash;
use std::mem::MaybeUninit;
use std::num::NonZeroU32;
use std::sync::Arc;

use fnv::FnvHashMap;
use parking_lot::RwLock;

use crate::types::InternKey;
use crate::types::RawInternKey;

/// An interner implementation that can be used to intern new values and lookup
/// the value of previously interned keys.
pub struct InternTable<K, V: 'static> {
    data: Arc<RwLock<InternTableData<K, V>>>,
}

impl<K, V> Default for InternTable<K, V>
where
    K: InternKey + Clone,
    V: Eq + Hash + Clone,
{
    fn default() -> Self {
        Self::new()
    }
}

impl<K, V> InternTable<K, V>
where
    K: InternKey + Clone,
    V: Eq + Hash + Clone,
{
    pub fn new() -> Self {
        Self {
            data: Arc::new(RwLock::new(InternTableData::new())),
        }
    }

    pub fn intern(&self, value: V) -> K {
        if let Some(prev) = self.data.read().get(&value) {
            return prev;
        }
        let mut writer = self.data.write();
        writer.intern(value)
    }

    pub fn lookup(&self, key: K) -> &'static V {
        self.data.read().lookup(key)
    }
}

/// Internal data used for public InternTable type.
struct InternTableData<K, V: 'static> {
    // Raw data storage, allocated in large chunks
    buffer: Option<&'static mut [MaybeUninit<V>]>,
    // Reverse mapping of index=>value, used to convert an
    // interned key back to (a reference to) its value
    items: Vec<&'static V>,
    // Mapping of values to their interned indices
    table: FnvHashMap<&'static V, K>,
}

// For some reason making this a const of InternTableData's impl block triggers
// an error that the size isn't known at compile time, but moving it here works:
// shrug, this works.
const INTERN_TABLE_BUFFER_SIZE: usize = 1000;

impl<K, V> InternTableData<K, V>
where
    K: InternKey + Clone,
    V: Eq + Hash + Clone,
{
    pub fn new() -> Self {
        Self {
            buffer: Some(Self::new_buffer()),
            items: Default::default(),
            table: Default::default(),
        }
    }

    fn new_buffer() -> &'static mut [MaybeUninit<V>] {
        // Create an uninitialized slice of values and leak it
        // to promote it to the static lifetime
        let buffer: [MaybeUninit<V>; INTERN_TABLE_BUFFER_SIZE] =
            unsafe { MaybeUninit::uninit().assume_init() };
        Box::leak(Box::new(buffer))
    }

    pub fn get(&self, value: &V) -> Option<K> {
        self.table.get(value).cloned()
    }

    pub fn intern(&mut self, value: V) -> K {
        // If there's an existing value return it
        if let Some(prev) = self.get(&value) {
            return prev;
        }
        // Ensure that there is sufficient space in the buffer for another item
        let mut buffer = self.buffer.take().unwrap();
        if buffer.is_empty() {
            buffer = Self::new_buffer();
        }
        // Split the buffer into a pointer to the first element and the remainder
        // of the slice, writing the new element into the first element pointer
        let (dest_ptr, remaining) = buffer.split_first_mut().unwrap();
        unsafe { std::ptr::write(dest_ptr, MaybeUninit::new(value)) };

        // Cast the mutable pointer to immutable
        let dest_ptr: &'static V = unsafe { &*dest_ptr.as_ptr() };

        // Create a key for this entry and update both the intern/lookup
        // mappings
        let key = K::from_raw(RawInternKey::new(unsafe {
            NonZeroU32::new_unchecked((self.items.len() + 1) as u32)
        }));
        self.items.push(dest_ptr);
        self.table.insert(dest_ptr, key.clone());
        self.buffer = Some(remaining);
        key
    }

    pub fn lookup(&self, key: K) -> &'static V {
        let index = key.into_raw().as_usize() - 1;
        self.items[index]
    }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

#![deny(warnings)]
#![deny(rust_2018_idioms)]
#![deny(clippy::all)]

mod bytes;
mod generic;
mod macros;
#[cfg(test)]
mod tests;
mod types;

pub use bytes::BytesKey;
pub use bytes::StringKey;
pub use generic::InternTable;
pub use types::Intern;
pub use types::InternKey;
pub use types::RawInternKey;

/// Re-exported values to be used by the `intern!` macro.
pub mod reexport {
    pub use once_cell::sync::Lazy;
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

/// Interns the passed string literal and memoizes the result in a static
/// variable. This can be used in performance sensitive code.
/// ```
/// use interner::intern;
/// assert_eq!(intern!("example").lookup(), "example");
/// ```
#[macro_export]
macro_rules! intern {
    ($value:literal) => {{
        use $crate::reexport::Lazy;
        use $crate::Intern;
        use $crate::StringKey;
        static KEY: Lazy<StringKey> = Lazy::new(|| Intern::intern($value));
        *KEY
    }};
    ($_:expr) => {
        compile_error!("intern! macro can only be used with string literals.")
    };
}

/// Macro to implement an interner for an arbitrary type.
/// Given `intern!(<Foo> as <FooKey>);`, this macro will implement the
/// `Intern` trait for `<Foo>`, interning it a generated `<FooKey>` wrapper
/// type. Calling `FooKey::lookup()` will return a reference to the original
/// `<Foo>` value.
///
/// # Example
///
/// ```ignore
/// use common::{Intern, intern, StringKey};
/// pub struct User {
///   name: StringKey,
/// }
/// intern!(User as UserKey); // defines the `UserKey` type
///
/// let name: StringKey = "Joe".intern();
/// let user: User = User { name };
/// let user_key: UserKey = user.intern();
/// ```
///
#[macro_export]
macro_rules! make_intern {
    ($name:ident as $alias:ident) => {
        use lazy_static::lazy_static;

        use crate::Intern;
        use crate::InternKey;
        use crate::InternTable;
        use crate::RawInternKey;

        lazy_static! {
            /// Global interning table for this type
            static ref INTERN_TABLE: InternTable<$alias, $name> = InternTable::new();
        }

        /// Wrapper type for the intern key
        #[derive(Copy, Clone, Debug, Eq, Ord, Hash, PartialEq, PartialOrd)]
        pub struct $alias(RawInternKey);

        impl InternKey for $alias {
            type Value = $name;

            fn from_raw(raw: RawInternKey) -> Self {
                Self(raw)
            }

            fn into_raw(self) -> RawInternKey {
                self.0
            }

            fn lookup(self) -> &'static Self::Value {
                INTERN_TABLE.lookup(self)
            }
        }

        /// The type interns into the generated key type
        impl Intern for $name {
            type Key = $alias;

            fn intern(self) -> Self::Key {
                INTERN_TABLE.intern(self)
            }
        }
    };
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

use super::*;

#[test]
fn test_string_intern() {
    let key: StringKey = "test".intern(); // str (reference)
    let key2: StringKey = "test".to_owned().intern(); // String (owned)
    assert_eq!(key2, key);
    assert_eq!(key.lookup(), "test");

    let key3: StringKey = "testx".to_owned().intern();
    assert_ne!(key3, key);
    assert_ne!(key3, key2);
    assert_eq!(key3.lookup(), "testx");
}

#[test]
fn test_bytes_intern() {
    let key: BytesKey = b"test".intern();
    let str_id: StringKey = "test".intern();
    let key2: BytesKey = str_id.as_bytes();
    assert_eq!(key2, key);
    assert_eq!(key.lookup(), b"test");

    let key3: BytesKey = b"testx".intern();
    assert_ne!(key3, key);
    assert_ne!(key3, key2);
    assert_eq!(key3.lookup(), b"testx");
}

#[test]
fn test_custom_intern() {
    #[derive(Clone, Eq, PartialEq, Hash, Ord, PartialOrd, Debug)]
    pub struct User {
        name: String,
    }

    make_intern!(User as UserKey);

    let key: UserKey = User {
        name: "Joe".to_owned(),
    }
    .intern();
    let key2: UserKey = User {
        name: "Joe".to_owned(),
    }
    .intern();
    assert_eq!(key2, key);
    assert_eq!(
        key.lookup(),
        &User {
            name: "Joe".to_owned()
        }
    );

    let key3: UserKey = User {
        name: "Jan".to_owned(),
    }
    .intern();
    assert_ne!(key3, key);
    assert_ne!(key3, key2);
    assert_eq!(
        key3.lookup(),
        &User {
            name: "Jan".to_owned()
        }
    );
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

use std::hash::Hash;
use std::num::NonZeroU32;

/// A type that can be interned.
pub trait Intern: Eq + Hash + Clone {
    type Key;

    fn intern(self) -> Self::Key;
}

/// A raw interned key; this should always be wrapped in a type-specific wrapper
/// that implements `InternKey`.
#[derive(Copy, Clone, Debug, Eq, Ord, Hash, PartialEq, PartialOrd)]
pub struct RawInternKey(NonZeroU32);

impl RawInternKey {
    #[inline(always)]
    pub(crate) const fn new(value: NonZeroU32) -> Self {
        Self(value)
    }

    pub(crate) fn as_usize(self) -> usize {
        self.0.get() as usize
    }
}

/// A type that acts as an intern key, uniquely identifying the original value
/// as well as supporting fast lookup back to a reference to the original value.
pub trait InternKey {
    type Value;

    fn from_raw(raw: RawInternKey) -> Self;

    fn into_raw(self) -> RawInternKey;

    fn lookup(self) -> &'static Self::Value;
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

use std::path::PathBuf;
use std::process::Output;

use thiserror::Error;

#[derive(Debug)]
pub struct ConfigError {
    pub path: PathBuf,
    pub code: ErrorCode,
}
impl std::fmt::Display for ConfigError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "Invalid config file: {:?}: {}", &self.path, &self.code)
    }
}

#[derive(Debug, Error)]
pub enum ErrorCode {
    #[error("Error parsing package.json: {error}")]
    PackageJsonParseError { error: serde_json::Error },

    #[error("Found key `{key}` in package.json, but failed incorrect value: {error}")]
    PackageJsonInvalidValue {
        key: String,
        error: serde_json::Error,
    },

    #[error("Error parsing JSON: {error}")]
    JsonParseError {
        #[from]
        error: serde_json::Error,
    },

    #[error("YAML file parsing not supported")]
    YamlFileUnsupported,

    #[error("Error running node: {}", String::from_utf8_lossy(&output.stderr))]
    NodeExecuteError { output: Output },
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

mod error;
mod loader;

use std::fmt::Display;
use std::path::Path;
use std::path::PathBuf;

pub use error::ConfigError;
pub use error::ErrorCode;
use loader::JsLoader;
use loader::JsonLoader;
use loader::Loader;
use loader::PackageJsonLoader;
use loader::YamlLoader;
use serde::Deserialize;

#[derive(Debug)]
pub struct Config<T> {
    pub path: PathBuf,
    pub value: T,
}

pub enum LoaderSource {
    PackageJson(String),
    Json(String),
    Js(String),
    Yaml(String),
}

impl Display for LoaderSource {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(
            f,
            "{}",
            match self {
                Self::PackageJson(name) => format!("`package.json` (\"{}\" key)", name),
                Self::Js(name) | Self::Json(name) | Self::Yaml(name) => format!("`{}`", name),
            }
        )
    }
}

pub fn load<T>(dir: &Path, sources: &[LoaderSource]) -> Result<Option<Config<T>>, ConfigError>
where
    T: for<'de> Deserialize<'de> + 'static,
{
    let mut loaders: Vec<(String, Box<dyn Loader<T>>)> = Vec::with_capacity(sources.len());
    for source in sources {
        match source {
            LoaderSource::PackageJson(name) => {
                loaders.push((
                    String::from("package.json"),
                    Box::new(PackageJsonLoader { key: name }),
                ));
            }
            LoaderSource::Js(name) => {
                loaders.push((name.clone(), Box::new(JsLoader)));
            }
            LoaderSource::Json(name) => {
                loaders.push((name.clone(), Box::new(JsonLoader)));
            }
            LoaderSource::Yaml(name) => {
                loaders.push((name.clone(), Box::new(YamlLoader)));
            }
        }
    }

    for search_dir in dir.ancestors() {
        for (file_name, loader) in &loaders {
            let file_path = search_dir.join(file_name);
            if file_path.exists() {
                match loader.load(&file_path) {
                    Ok(None) => {}
                    Ok(Some(value)) => {
                        return Ok(Some(Config {
                            path: file_path,
                            value,
                        }));
                    }
                    Err(code) => {
                        return Err(ConfigError {
                            path: file_path,
                            code,
                        });
                    }
                };
            }
        }
    }

    Ok(None)
}

pub fn search<T>(name: &str, dir: &Path) -> Result<Option<Config<T>>, ConfigError>
where
    T: for<'de> Deserialize<'de> + 'static,
{
    load(
        dir,
        &[
            LoaderSource::PackageJson(name.to_string()),
            LoaderSource::Json(format!(".{}rc", name)),
            LoaderSource::Json(format!("{}.config.json", name)),
            LoaderSource::Yaml(format!(".{}rc.yaml", name)),
            LoaderSource::Yaml(format!(".{}rc.yml", name)),
            LoaderSource::Js(format!(".{}.rc.js", name)),
            LoaderSource::Js(format!(".{}.rc.cjs", name)),
            LoaderSource::Js(format!("{}.config.js", name)),
            LoaderSource::Js(format!("{}.config.cjs", name)),
        ],
    )
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

use std::fs::File;
use std::io::BufReader;
use std::path::Path;
use std::process::Command;

use serde::Deserialize;
use serde_json::Value;

use crate::ErrorCode;

pub trait Loader<T> {
    fn load(&self, path: &Path) -> Result<Option<T>, ErrorCode>;
}

pub struct PackageJsonLoader<'a> {
    pub key: &'a str,
}
impl<'a, T: for<'de> Deserialize<'de>> Loader<T> for PackageJsonLoader<'a> {
    fn load(&self, path: &Path) -> Result<Option<T>, ErrorCode> {
        let file = File::open(&path).unwrap();
        let reader = BufReader::new(file);
        let mut package_json: Value = serde_json::from_reader(reader)
            .map_err(|error| ErrorCode::PackageJsonParseError { error })?;
        if let Some(config_value) = package_json.get_mut(self.key) {
            match serde_json::from_value::<T>(config_value.take()) {
                Ok(config) => Ok(Some(config)),
                Err(error) => Err(ErrorCode::PackageJsonInvalidValue {
                    key: self.key.into(),
                    error,
                }),
            }
        } else {
            Ok(None)
        }
    }
}

pub struct YamlLoader;
impl<T> Loader<T> for YamlLoader {
    fn load(&self, _path: &Path) -> Result<Option<T>, ErrorCode> {
        Err(ErrorCode::YamlFileUnsupported)
    }
}

pub struct JsonLoader;
impl<T: for<'de> Deserialize<'de> + 'static> Loader<T> for JsonLoader {
    fn load(&self, path: &Path) -> Result<Option<T>, ErrorCode> {
        let file = File::open(&path).unwrap();
        let reader = BufReader::new(file);
        let config = serde_json::from_reader(reader)?;
        Ok(Some(config))
    }
}

pub struct JsLoader;
impl<T: for<'de> Deserialize<'de> + 'static> Loader<T> for JsLoader {
    fn load(&self, path: &Path) -> Result<Option<T>, ErrorCode> {
        let output = Command::new("node")
            .arg("-e")
            .arg(r#"process.stdout.write(JSON.stringify(require(process.argv[1])))"#)
            .arg(&path)
            .output()
            .expect("failed to execute process. Make sure you have Node installed.");

        if output.status.success() {
            let value = serde_json::from_slice::<T>(&output.stdout);
            Ok(Some(value.unwrap()))
        } else {
            Err(ErrorCode::NodeExecuteError { output })
        }
    }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

use std::fs::create_dir_all;

use js_config_loader::search;
use js_config_loader::ConfigError;
use js_config_loader::ErrorCode;
use serde::Deserialize;
use tempfile::tempdir;

#[derive(Debug, Deserialize)]
struct TestConfig {
    name: String,
}

#[test]
fn package_json_and_rc_same_dir() {
    let dir = tempdir().unwrap();
    let dir_d = dir.path().join("a/b/c/d");
    let dir_f = dir.path().join("a/b/c/d/e/f");
    create_dir_all(&dir_f).unwrap();

    std::fs::write(dir_d.join("package.json"), r#"{ "false": "hope" }"#).unwrap();
    std::fs::write(dir_d.join(".foorc"), r#"{ "name": "correct" }"#).unwrap();

    let config = search::<TestConfig>("foo", &dir_f).unwrap().unwrap();
    assert_eq!(config.value.name, "correct");
}

#[test]
fn rc_json_invalid_syntax() {
    let dir = tempdir().unwrap();
    let dir_d = dir.path().join("a/b/c/d");
    let dir_f = dir.path().join("a/b/c/d/e/f");
    create_dir_all(&dir_f).unwrap();

    std::fs::write(dir_d.join(".foorc"), "invalid").unwrap();

    match search::<TestConfig>("foo", &dir_f) {
        Err(ConfigError {
            code: ErrorCode::JsonParseError { error },
            path,
        }) => {
            assert!(error.is_syntax());
            assert_eq!(path.file_name().unwrap(), ".foorc");
        }
        other => {
            panic!("unexpected value: {:?}", other);
        }
    }
}

#[test]
fn package_json() {
    let dir = tempdir().unwrap();
    let dir_d = dir.path().join("a/b/c/d");
    let dir_f = dir.path().join("a/b/c/d/e/f");
    create_dir_all(&dir_f).unwrap();

    std::fs::write(
        dir_d.join("package.json"),
        r#"
            {
                "foo": { "name": "correct" }
            }
        "#,
    )
    .unwrap();
    std::fs::write(dir_d.join(".foorc"), r#"{ "name": "wrong" }"#).unwrap();

    let config = search::<TestConfig>("foo", &dir_f).unwrap().unwrap();
    assert_eq!(config.value.name, "correct");
}

#[test]
fn package_json_invalid_syntax() {
    let dir = tempdir().unwrap();
    let dir_d = dir.path().join("a/b/c/d");
    let dir_f = dir.path().join("a/b/c/d/e/f");
    create_dir_all(&dir_f).unwrap();

    std::fs::write(dir_d.join("package.json"), "invalid").unwrap();
    std::fs::write(dir_d.join(".foorc"), r#"{ "name": "wrong" }"#).unwrap();

    match search::<TestConfig>("foo", &dir_f).unwrap_err() {
        ConfigError {
            path: _,
            code: ErrorCode::PackageJsonParseError { error },
        } => {
            assert!(error.is_syntax());
        }
        other => panic!("incorrect error: {:?}", other),
    }
}

#[test]
fn package_json_invalid_format() {
    let dir = tempdir().unwrap();
    let dir_d = dir.path().join("a/b/c/d");
    let dir_f = dir.path().join("a/b/c/d/e/f");
    create_dir_all(&dir_f).unwrap();

    std::fs::write(
        dir_d.join("package.json"),
        r#"
            {
                "foo": { "incorrect": 1 }
            }
        "#,
    )
    .unwrap();
    std::fs::write(dir_d.join(".foorc"), r#"{ "name": "wrong" }"#).unwrap();

    match search::<TestConfig>("foo", &dir_f).unwrap_err() {
        ConfigError {
            path: _,
            code: ErrorCode::PackageJsonInvalidValue { key, error },
        } => {
            assert_eq!(key, "foo");
            assert!(error.is_data());
        }
        other => panic!("incorrect error: {:?}", other),
    }
}

#[test]
fn config_js() {
    let dir = tempdir().unwrap();
    let dir_d = dir.path().join("a/b/c/d");
    let dir_f = dir.path().join("a/b/c/d/e/f");
    create_dir_all(&dir_f).unwrap();

    std::fs::write(
        dir_d.join("foo.config.js"),
        r#"
        module.exports = { name: "correct" };
        "#,
    )
    .unwrap();

    let config = search::<TestConfig>("foo", &dir_f).unwrap().unwrap();
    assert_eq!(config.value.name, "correct");
}

#[test]
fn config_js_invalid_js() {
    let dir = tempdir().unwrap();
    let dir_d = dir.path().join("a/b/c/d");
    let dir_f = dir.path().join("a/b/c/d/e/f");
    create_dir_all(&dir_f).unwrap();

    std::fs::write(
        dir_d.join("foo.config.js"),
        r#"
        module.exports = foo();
        "#,
    )
    .unwrap();

    match search::<TestConfig>("foo", &dir_f).unwrap_err() {
        ConfigError {
            code: ErrorCode::NodeExecuteError { output: _ },
            path,
        } => {
            assert_eq!(path.file_name().unwrap(), "foo.config.js");
        }
        other => panic!("incorrect error: {:?}", other),
    }
}

#[test]
fn unsupported_file() {
    let dir = tempdir().unwrap();
    let dir_d = dir.path().join("a/b/c/d");
    let dir_f = dir.path().join("a/b/c/d/e/f");
    create_dir_all(&dir_f).unwrap();

    std::fs::write(dir_d.join(".foorc.yaml"), "name: correct").unwrap();

    match search::<TestConfig>("foo", &dir_f).unwrap_err() {
        ConfigError {
            code: ErrorCode::YamlFileUnsupported,
            path,
        } => {
            assert_eq!(path.file_name().unwrap(), ".foorc.yaml");
        }
        other => panic!("incorrect error: {:?}", other),
    }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

use thiserror::Error;

#[derive(Debug, Error)]
pub enum PersistError {
    #[error("Network create error: {error}")]
    NetworkCreateError {
        error: Box<dyn std::error::Error + Send>,
    },

    #[error("Network error: {source}")]
    NetworkError {
        #[from]
        source: hyper::Error,
    },

    #[error("Persisting failed: {message}")]
    ErrorResponse { message: String },

    #[error("Failed parsing response: {source}")]
    ResponseParseError {
        #[from]
        source: serde_json::Error,
    },

    #[error("IO Error: {source}")]
    IOError {
        #[from]
        source: std::io::Error,
    },
}