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

//! Additional [`PhantomData`](PhantomData) related types.

use std::cell::Cell;
use std::fmt;
use std::fmt::Debug;
use std::hash::Hash;
use std::hash::Hasher;
use std::marker::PhantomData;

use dupe::Dupe;

/// A type like [`PhantomData`](PhantomData), but where the contained `T` is invariant
/// in both lifetimes and types. See [variance on the Nomicon](https://doc.rust-lang.org/nomicon/subtyping.html#variance) for an
/// explanation of these terms.
pub struct PhantomDataInvariant<T: ?Sized>(PhantomData<Cell<T>>);

impl<T: ?Sized> PhantomDataInvariant<T> {
    #[inline]
    pub const fn new() -> Self {
        PhantomDataInvariant(PhantomData)
    }
}

impl<T: ?Sized> Debug for PhantomDataInvariant<T> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::fmt::Result {
        f.pad("PhantomDataInvariant")
    }
}

impl<T: ?Sized> Default for PhantomDataInvariant<T> {
    fn default() -> Self {
        Self::new()
    }
}

impl<T: ?Sized> Clone for PhantomDataInvariant<T> {
    fn clone(&self) -> Self {
        *self
    }
}

impl<T: ?Sized> Copy for PhantomDataInvariant<T> {}

impl<T: ?Sized> Dupe for PhantomDataInvariant<T> {}

impl<T: ?Sized> Eq for PhantomDataInvariant<T> {}

impl<T: ?Sized> Hash for PhantomDataInvariant<T> {
    fn hash<H: Hasher>(&self, state: &mut H) {
        self.0.hash(state)
    }
}

impl<T: ?Sized> Ord for PhantomDataInvariant<T> {
    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
        self.0.cmp(&other.0)
    }
}

impl<T: ?Sized> PartialEq<PhantomDataInvariant<T>> for PhantomDataInvariant<T> {
    fn eq(&self, other: &PhantomDataInvariant<T>) -> bool {
        self.0.eq(&other.0)
    }
}

impl<T: ?Sized> PartialOrd<PhantomDataInvariant<T>> for PhantomDataInvariant<T> {
    fn partial_cmp(&self, other: &PhantomDataInvariant<T>) -> Option<std::cmp::Ordering> {
        self.0.partial_cmp(&other.0)
    }
}


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

//! Standard functions. Usually imported with `use gazebo::prelude::*`.
//!
//! Contains:
//!
//! * Extension methods for [`str`](str) and slice/[`Vec`](Vec).
//! * Defines [`Default_`] macro.
//!
//! The derivation macros appended with underscore are like the normal
//! derivations, but don't require the trait on any argument types.
//! For example, given the type:
//!
//! ```rust
//! # use gazebo::prelude::*;
//! #[derive(Default_)]
//! struct Foo<T>(std::marker::PhantomData<T>);
//! ```
pub use gazebo_derive::Default_;

pub use crate::ext::iter::IterExt;
pub use crate::ext::iter::IterOwned;
pub use crate::ext::option::OptionExt;
pub use crate::ext::str::StrExt;
pub use crate::ext::vec::SliceClonedExt;
pub use crate::ext::vec::SliceCopiedExt;
pub use crate::ext::vec::SliceDupedExt;
pub use crate::ext::vec::SliceExt;
pub use crate::ext::vec::VecExt;

#[cfg(test)]
mod tests {
    use std::sync::Arc;

    use dupe::Clone_;

    struct NoTraits();

    #[derive(Clone_)]
    struct Foo<A> {
        foo: Arc<A>,
    }

    #[test]
    fn test() {
        let x = Foo {
            foo: Arc::new(NoTraits()),
        };
        let x2 = x.clone();
        // Now make it clear to clippy that all those clones were important
        std::mem::drop(x2);
        std::mem::drop(x);
    }
}


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

use std::collections::HashMap;

use crate::prelude::*;

enum NoDefault {
    _None,
}

#[derive(Default_)]
struct Foo<K, V, Z> {
    mp: HashMap<K, V>,
    xs: Vec<Z>,
}

#[derive(Default_, Debug, PartialEq)]
struct Bar;

#[test]
fn test_default_() {
    let x: Foo<NoDefault, NoDefault, NoDefault> = Default::default();
    assert_eq!(x.mp.len(), 0);
    assert_eq!(x.xs.len(), 0);

    assert_eq!(Bar, Default::default());
}


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

//! Operations working on Rust types.

// If we need more type operations we should probably model them on
// https://hackage.haskell.org/package/base-4.12.0.0/docs/Data-Type-Equality.html

/// A trait witnessing that two types are equal.
///
/// For example:
///
/// ```
/// use gazebo::types::TEq;
/// fn foo<A : TEq<String>>(x: A) -> String {
///    x.teq()
/// }
/// ```
///
/// You should not make any further implementations of this trait.
///
/// Originally taken from a request to have Rust allow equality constraints in
/// traits, from [Issue 20041](https://github.com/rust-lang/rust/issues/20041#issuecomment-414551783).
pub trait TEq<T> {
    /// Convert between two equal types.
    fn teq(self) -> T;
    /// Convert between references to two equal types.
    fn teq_ref(&self) -> &T;
    /// Convert between mutable references to two equal types.
    fn teq_mut(&mut self) -> &mut T;
}

impl<T> TEq<T> for T {
    fn teq(self) -> Self {
        self
    }
    fn teq_ref(&self) -> &Self {
        self
    }
    fn teq_mut(&mut self) -> &mut T {
        self
    }
}


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

//! Working with the variants of an `enum`.

/// Generates implementation to unpack the inner data of enum variants. The `unpack_XXX` methods
/// return the unpacked data as a tuple of references of the inner data. The `into_XXX` variants
/// return similarly-structured tuples.
///
/// ```
/// use gazebo::variants::UnpackVariants;
///
/// #[derive(UnpackVariants)]
/// enum MyEnum {
///     Unit,
///     Unnamed(String, usize),
///     Named { x: String, y: usize },
///     OneWithCapInName(bool),
/// }
///
/// let x = MyEnum::Unit;
/// assert_eq!(x.unpack_unit(), Some(()));
/// assert_eq!(x.unpack_unnamed(), None);
/// assert_eq!(x.unpack_named(), None);
/// assert_eq!(x.unpack_one_with_cap_in_name(), None);
///
/// let x = MyEnum::Unnamed("foo".into(), 1);
/// assert_eq!(x.unpack_unnamed(), Some((&"foo".to_owned(), &1usize)));
/// assert_eq!(x.unpack_unit(), None);
/// assert_eq!(x.unpack_named(), None);
/// assert_eq!(x.unpack_one_with_cap_in_name(), None);
///
/// let x = MyEnum::Named {
///     x: "foo".into(),
///     y: 2,
/// };
/// assert_eq!(x.unpack_unit(), None);
/// assert_eq!(x.unpack_unnamed(), None);
/// assert_eq!(x.unpack_named(), Some((&"foo".to_owned(), &2usize)));
/// assert_eq!(x.unpack_one_with_cap_in_name(), None);
///
/// let x = MyEnum::OneWithCapInName(true);
/// assert_eq!(x.unpack_one_with_cap_in_name(), Some(&true));
/// assert_eq!(x.unpack_unit(), None);
/// assert_eq!(x.unpack_unnamed(), None);
/// assert_eq!(x.unpack_named(), None);
///
/// let x = MyEnum::Unnamed("foo".into(), 1);
/// assert_eq!(x.into_unnamed(), Some(("foo".to_owned(), 1usize)));
///
/// let x = MyEnum::Unit;
/// assert_eq!(x.into_unit(), Some(()));
///
/// let x = MyEnum::Named {
///     x: "foo".into(),
///     y: 2,
/// };
/// assert_eq!(x.into_named(), Some(("foo".into(), 2usize)));
///
/// let x = MyEnum::OneWithCapInName(true);
/// assert_eq!(x.into_one_with_cap_in_name(), Some(true));
///
/// assert_eq!(MyEnum::Unit.into_unnamed(), None);
/// assert_eq!(MyEnum::Unit.into_named(), None);
/// assert_eq!(MyEnum::Unit.into_one_with_cap_in_name(), None);
/// ```
pub use gazebo_derive::UnpackVariants;
/// Trait for enums to return the name of the current variant as a `str`. Useful for
/// debugging messages.
///
/// ```
/// use gazebo::variants::VariantName;
///
/// #[derive(VariantName)]
/// enum Foo {
///     Bar,
///     Baz(usize),
///     Qux { i: usize },
/// }
///
/// assert_eq!(Foo::Bar.variant_name(), "Bar");
/// assert_eq!(Foo::Baz(1).variant_name(), "Baz");
/// assert_eq!(Foo::Qux { i: 1 }.variant_name(), "Qux");
/// ```
///
pub use gazebo_derive::VariantName;

pub trait VariantName {
    fn variant_name(&self) -> &'static str;
}

impl<T> VariantName for Option<T> {
    fn variant_name(&self) -> &'static str {
        match self {
            Self::Some(_) => "Some",
            None => "None",
        }
    }
}

impl<T, E> VariantName for Result<T, E> {
    fn variant_name(&self) -> &'static str {
        match self {
            Self::Ok(_) => "Ok",
            Self::Err(_) => "Err",
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    #[allow(unused_imports)] // Not actually unused, this makes testing the derive macro work
    use crate as gazebo;

    #[test]
    fn derive_variant_names() {
        #[allow(unused)] // The fields aren't used, only the variant names
        #[derive(VariantName)]
        enum MyEnum {
            Foo,
            Bar(usize),
            Baz { field: usize },
        }

        let x = MyEnum::Foo;
        assert_eq!(x.variant_name(), "Foo");

        let x = MyEnum::Bar(1);
        assert_eq!(x.variant_name(), "Bar");

        let x = MyEnum::Baz { field: 1 };
        assert_eq!(x.variant_name(), "Baz");
    }
}


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

use std::cmp::Ordering;

/// Extension traits on [`Iterator`](Iterator).
pub trait IterExt {
    type Item;

    /// Like `any`, except allow the function supplied to return a `Result` type, where we `Err`
    /// on the first encounter of `Err`.
    ///
    /// ```
    /// use gazebo::prelude::*;
    ///
    /// fn true_if_even_throw_on_zero(x: &usize) -> Result<bool, ()> {
    ///     if *x == 0 {
    ///         Err(())
    ///     } else {
    ///         Ok(x % 2 == 0)
    ///     }
    /// }
    ///
    /// let x = [1, 3, 2];
    /// assert_eq!(x.iter().try_any(true_if_even_throw_on_zero), Ok(true));
    ///
    /// let x = [1, 3, 5];
    /// assert_eq!(x.iter().try_any(true_if_even_throw_on_zero), Ok(false));
    ///
    /// let x = [1, 0, 2];
    /// assert_eq!(x.iter().try_any(true_if_even_throw_on_zero), Err(()));
    ///
    /// ```
    fn try_any<F, E>(self, any: F) -> Result<bool, E>
    where
        Self: Sized,
        F: FnMut(Self::Item) -> Result<bool, E>;

    /// Like `all`, except allow the function supplied to return a `Result` type, where we `Err`
    /// on the first encounter of `Err`.
    ///
    /// ```
    /// use gazebo::prelude::*;
    ///
    /// fn true_if_even_throw_on_zero(x: &usize) -> Result<bool, ()> {
    ///     if *x == 0 {
    ///         Err(())
    ///     } else {
    ///         Ok(x % 2 == 0)
    ///     }
    /// }
    ///
    /// let x = [2, 4, 2];
    /// assert_eq!(x.iter().try_all(true_if_even_throw_on_zero), Ok(true));
    ///
    /// let x = [1, 3, 5];
    /// assert_eq!(x.iter().try_all(true_if_even_throw_on_zero), Ok(false));
    ///
    /// let x = [2, 0, 2];
    /// assert_eq!(x.iter().try_all(true_if_even_throw_on_zero), Err(()));
    ///
    /// ```
    fn try_all<F, E>(self, any: F) -> Result<bool, E>
    where
        Self: Sized,
        F: FnMut(Self::Item) -> Result<bool, E>;

    /// Like `eq_by`, except allow the function supplied to return a `Result` type, where we `Err`
    /// on the first encounter of `Err`.
    ///
    /// ```
    /// use gazebo::prelude::*;
    ///
    /// fn double_eq_throw_on_zero(x: &usize, y: &usize) -> Result<bool, ()> {
    ///     if *x == 0 || *y == 0 {
    ///         Err(())
    ///     } else {
    ///         Ok(x * 2 == *y)
    ///     }
    /// }
    ///
    /// let x = [1, 4, 2];
    /// let y = [2, 8, 4];
    ///
    /// assert_eq!(x.iter().try_eq_by(&y, double_eq_throw_on_zero), Ok(true));
    ///
    /// let x = [1, 4, 2];
    /// let y = [2, 0, 4];
    ///
    /// assert_eq!(x.iter().try_eq_by(&y, double_eq_throw_on_zero), Err(()));
    /// ```
    fn try_eq_by<I, F, E>(self, other: I, eq: F) -> Result<bool, E>
    where
        Self: Sized,
        I: IntoIterator,
        F: FnMut(Self::Item, I::Item) -> Result<bool, E>;

    /// Like `cmp_by`, except allow the function supplied to return a `Result` type, where we `Err`
    /// on the first encounter of `Err`.
    ///
    /// ```
    /// use gazebo::prelude::*;
    /// use std::cmp::Ordering;
    ///
    /// fn double_cmp_throw_on_zero(x: &usize, y: &usize) -> Result<Ordering, ()> {
    ///     if *x == 0 || *y == 0 {
    ///         Err(())
    ///     } else {
    ///         Ok((x * 2).cmp(y))
    ///     }
    /// }
    ///
    /// let x = [1, 4, 2];
    /// let y = [2, 8, 4];
    ///
    /// assert_eq!(x.iter().try_cmp_by(&y, double_cmp_throw_on_zero), Ok(Ordering::Equal));
    ///
    /// let x = [1, 2, 2];
    /// let y = [2, 8, 4];
    ///
    /// assert_eq!(x.iter().try_cmp_by(&y, double_cmp_throw_on_zero), Ok(Ordering::Less));
    ///
    /// let x = [1, 4];
    /// let y = [2, 8, 4];
    ///
    /// assert_eq!(x.iter().try_cmp_by(&y, double_cmp_throw_on_zero), Ok(Ordering::Less));
    ///
    /// let x = [1, 4, 4];
    /// let y = [2, 8, 4];
    ///
    /// assert_eq!(x.iter().try_cmp_by(&y, double_cmp_throw_on_zero), Ok(Ordering::Greater));
    ///
    /// let x = [1, 4, 2, 3];
    /// let y = [2, 8, 4];
    ///
    /// assert_eq!(x.iter().try_cmp_by(&y, double_cmp_throw_on_zero), Ok(Ordering::Greater));
    ///
    /// let x = [1, 4, 2];
    /// let y = [2, 0, 4];
    ///
    /// assert_eq!(x.iter().try_cmp_by(&y, double_cmp_throw_on_zero), Err(()));
    /// ```
    fn try_cmp_by<I, F, E>(self, other: I, cmp: F) -> Result<Ordering, E>
    where
        Self: Sized,
        I: IntoIterator,
        F: FnMut(Self::Item, I::Item) -> Result<Ordering, E>;

    /// Like `unzip`, except allowing the current `Iterator` to contain `Result` type, where we `Err`
    /// on the first encounter of `Err`.
    ///
    /// ```
    /// use gazebo::prelude::*;
    ///
    /// let i = vec![Ok::<_, ()>((1, "a")), Ok((2, "b"))];
    ///
    /// assert_eq!(i.into_iter().try_unzip(), Ok((vec![1, 2], vec!["a", "b"])));
    ///
    /// let i = vec![Ok((1, "a")), Err(()), Ok((2, "b"))];
    ///
    /// assert_eq!(i.into_iter().try_unzip::<_, _, Vec<_>, Vec<_>, _>(), Err(()));
    /// ```
    fn try_unzip<A, B, FromA, FromB, E>(self) -> Result<(FromA, FromB), E>
    where
        FromA: Default + Extend<A>,
        FromB: Default + Extend<B>,
        Self: Iterator<Item = Result<(A, B), E>>;

    /// If this iterator contains a single element, return it. Otherwise, return `None`.
    ///
    /// ```
    /// use gazebo::prelude::*;
    ///
    /// let i = vec![1];
    /// assert_eq!(i.into_iter().into_singleton(), Some(1));
    ///
    /// let i = Vec::<i64>::new();
    /// assert_eq!(i.into_iter().into_singleton(), None);
    ///
    /// let i = vec![1, 2];
    /// assert_eq!(i.into_iter().into_singleton(), None);
    /// ```
    fn into_singleton(self) -> Option<Self::Item>
    where
        Self: Sized;
}

pub trait IterOwned: Sized {
    /// Calls `to_owned()` on all the items provided by the inner Iterator.
    ///
    /// ```
    /// use gazebo::prelude::*;
    ///
    /// let inputs = vec!["a", "b", "c"];
    /// let outputs = inputs.into_iter().owned().collect::<Vec<_>>();
    /// assert_eq!(outputs, vec!["a".to_owned(), "b".to_owned(), "c".to_owned()])
    /// ```
    fn owned(self) -> Owned<Self>;
}

impl<I> IterExt for I
where
    I: Iterator,
{
    type Item = I::Item;

    fn try_any<F, E>(mut self, f: F) -> Result<bool, E>
    where
        Self: Sized,
        F: FnMut(Self::Item) -> Result<bool, E>,
    {
        // TODO migrate use of Result<(), Option<E>> to ControlFlow when it's no longer unstable
        fn check<T, E>(
            mut f: impl FnMut(T) -> Result<bool, E>,
        ) -> impl FnMut((), T) -> Result<(), Option<E>> {
            move |(), x| match f(x) {
                Ok(true) => Err(None),
                Ok(false) => Ok(()),
                Err(e) => Err(Some(e)),
            }
        }

        match self.try_fold((), check(f)) {
            Ok(()) => Ok(false),
            Err(None) => Ok(true),
            Err(Some(e)) => Err(e),
        }
    }

    fn try_all<F, E>(mut self, f: F) -> Result<bool, E>
    where
        Self: Sized,
        F: FnMut(Self::Item) -> Result<bool, E>,
    {
        // TODO migrate use of Result<(), Option<E>> to ControlFlow when it's no longer unstable
        fn check<T, E>(
            mut f: impl FnMut(T) -> Result<bool, E>,
        ) -> impl FnMut((), T) -> Result<(), Option<E>> {
            move |(), x| match f(x) {
                Ok(true) => Ok(()),
                Ok(false) => Err(None),
                Err(e) => Err(Some(e)),
            }
        }

        match self.try_fold((), check(f)) {
            Ok(()) => Ok(true),
            Err(None) => Ok(false),
            Err(Some(e)) => Err(e),
        }
    }

    fn try_eq_by<O, F, E>(mut self, other: O, mut eq: F) -> Result<bool, E>
    where
        Self: Sized,
        O: IntoIterator,
        F: FnMut(Self::Item, O::Item) -> Result<bool, E>,
    {
        let mut other = other.into_iter();

        loop {
            let x = match self.next() {
                None => return Ok(other.next().is_none()),
                Some(val) => val,
            };

            let y = match other.next() {
                None => return Ok(false),
                Some(val) => val,
            };

            if !eq(x, y)? {
                return Ok(false);
            }
        }
    }

    fn try_cmp_by<O, F, E>(mut self, other: O, mut cmp: F) -> Result<Ordering, E>
    where
        Self: Sized,
        O: IntoIterator,
        F: FnMut(Self::Item, O::Item) -> Result<Ordering, E>,
    {
        let mut other = other.into_iter();

        loop {
            let x = match self.next() {
                None => {
                    if other.next().is_none() {
                        return Ok(Ordering::Equal);
                    } else {
                        return Ok(Ordering::Less);
                    }
                }
                Some(val) => val,
            };

            let y = match other.next() {
                None => return Ok(Ordering::Greater),
                Some(val) => val,
            };

            match cmp(x, y)? {
                Ordering::Equal => {}
                non_eq => return Ok(non_eq),
            }
        }
    }

    fn try_unzip<A, B, FromA, FromB, E>(self) -> Result<(FromA, FromB), E>
    where
        FromA: Default + Extend<A>,
        FromB: Default + Extend<B>,
        Self: Iterator<Item = Result<(A, B), E>>,
    {
        let mut ts: FromA = Default::default();
        let mut us: FromB = Default::default();

        for e in self {
            let (t, u) = e?;
            ts.extend(Some(t));
            us.extend(Some(u));
        }

        Ok((ts, us))
    }

    fn into_singleton(mut self) -> Option<Self::Item>
    where
        Self: Sized,
    {
        let ret = self.next()?;
        if self.next().is_some() {
            return None;
        }
        Some(ret)
    }
}

impl<'a, I, T> IterOwned for I
where
    I: Iterator<Item = &'a T> + Sized,
    T: 'a + ToOwned + ?Sized,
{
    fn owned(self) -> Owned<Self> {
        Owned { inner: self }
    }
}

/// An Iterator that yields the Owned variants of the inner iterator's items.
pub struct Owned<I> {
    inner: I,
}

impl<'a, I, T> Iterator for Owned<I>
where
    I: Iterator<Item = &'a T>,
    T: 'a + ToOwned + ?Sized,
{
    type Item = <T as ToOwned>::Owned;

    fn next(&mut self) -> Option<Self::Item> {
        Some(self.inner.next()?.to_owned())
    }
}


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

pub(crate) mod iter;
pub(crate) mod option;
pub(crate) mod str;
pub(crate) mod vec;


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

/// Extension traits on [`Option`](Option) where it holds any value or ref.
pub trait OptionExt {
    type Item;

    /// Like `map`, but as a `Result`
    ///
    /// ```
    /// use gazebo::prelude::*;
    ///
    /// assert_eq!(Some("foo").try_map(|x| Ok::<_, ()>(x.len())), Ok(Some(3)));
    /// assert_eq!(Some("foo").try_map(|x| Err::<(), _>(())), Err(()));
    /// ```
    fn try_map<U, E, F: FnOnce(Self::Item) -> Result<U, E>>(self, f: F) -> Result<Option<U>, E>;
}

impl<T> OptionExt for Option<T> {
    type Item = T;

    fn try_map<U, E, F: FnOnce(Self::Item) -> Result<U, E>>(self, f: F) -> Result<Option<U>, E> {
        self.map(f).transpose()
    }
}


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

#[cfg(feature = "str_pattern_extensions")]
use std::str::pattern::*;

/// Extension traits on [`str`](str).
///
/// Set the configuration option `str_pattern_extensions` to enable the associated methods.
/// The setting `str_pattern_extensions` requires the unstable features
/// `pattern` and `associated_type_bounds`, so only works with Rust nightly.
pub trait StrExt {
    /// Like `split`, but only separates off the first element. For example:
    ///
    /// ```
    /// use gazebo::prelude::*;
    /// assert_eq!("test".split1('e'), ("t", "st"));
    /// assert_eq!("test".split1('t'), ("", "est"));
    /// assert_eq!("test".split1('x'), ("test", ""));
    /// assert_eq!("".split1('e'), ("", ""));
    /// ```
    #[cfg(feature = "str_pattern_extensions")]
    fn split1<'a, P>(&'a self, pat: P) -> (&'a Self, &'a Self)
    where
        P: Pattern<'a>;

    /// Like `split`, but only separates off the first element if there is a
    /// separator, otherwise returns `None`. For example:
    ///
    /// ```
    /// use gazebo::prelude::*;
    /// assert_eq!("test".split1_opt('e'), Some(("t", "st")));
    /// assert_eq!("test".split1_opt('t'), Some(("", "est")));
    /// assert_eq!("test".split1_opt('x'), None);
    /// assert_eq!("test!".split1_opt('!'), Some(("test", "")));
    /// assert_eq!("test".split1_opt("es"), Some(("t", "t")));
    /// assert_eq!("".split1_opt('e'), None);
    /// ```
    ///
    /// In most cases you should use `split_once`, which is now available in `std`.
    #[cfg(feature = "str_pattern_extensions")]
    #[deprecated(since = "0.6.1", note = "use `split_once` available in `std`")]
    fn split1_opt<'a, P>(&'a self, pat: P) -> Option<(&'a Self, &'a Self)>
    where
        P: Pattern<'a>;

    /// Trim off the first match, or return the string unchanged if the pattern
    /// is not a prefix of the string. Like 'trim_start_matches', but at
    /// most one trim.
    ///
    /// ```
    /// use gazebo::prelude::*;
    /// assert_eq!("test".trim_start_match("tes"), "t");
    /// assert_eq!("test".trim_start_match("x"), "test");
    /// assert_eq!("tttest".trim_start_match("t"), "ttest");
    /// ```
    #[cfg(feature = "str_pattern_extensions")]
    fn trim_start_match<'a, P>(&'a self, pat: P) -> &'a Self
    where
        P: Pattern<'a>;

    /// Trim off the first match and return 'Some', or return 'None' if the
    /// pattern is not a prefix of the string. Like 'trim_start_matches'.
    ///
    /// ```
    /// use gazebo::prelude::*;
    /// assert_eq!("test".trim_start_match_opt("tes"), Some("t"));
    /// assert_eq!("test".trim_start_match_opt("x"), None);
    /// assert_eq!("tttest".trim_start_match_opt("t"), Some("ttest"));
    /// assert_eq!("est".trim_start_match_opt("t"), None);
    /// ```
    #[cfg(feature = "str_pattern_extensions")]
    #[deprecated(note = "Use str.strip_prefix instead")]
    fn trim_start_match_opt<'a, P>(&'a self, pat: P) -> Option<&'a Self>
    where
        P: Pattern<'a>;

    /// Trim off the first match, or return the string unchanged if the pattern
    /// is not a prefix of the string. Like 'trim_start_matches', but at
    /// most one trim.
    ///
    /// ```
    /// use gazebo::prelude::*;
    /// assert_eq!("test".trim_end_match("est"), "t");
    /// assert_eq!("test".trim_end_match("x"), "test");
    /// assert_eq!("testtt".trim_end_match("t"), "testt");
    /// ```
    #[cfg(feature = "str_pattern_extensions")]
    fn trim_end_match<'a, P>(&'a self, pat: P) -> &'a Self
    where
        P: Pattern<'a, Searcher: ReverseSearcher<'a>>;

    /// Trim off the first match and return 'Some', or return 'None' if the
    /// pattern is not a prefix of the string. Like 'trim_start_matches'.
    ///
    /// ```
    /// use gazebo::prelude::*;
    /// assert_eq!("test".trim_end_match_opt("est"), Some("t"));
    /// assert_eq!("test".trim_end_match_opt("x"), None);
    /// assert_eq!("testtt".trim_end_match_opt("t"), Some("testt"));
    /// assert_eq!("tes".trim_end_match_opt("t"), None);
    /// ```
    #[cfg(feature = "str_pattern_extensions")]
    #[deprecated(note = "Use str.strip_suffix instead")]
    fn trim_end_match_opt<'a, P>(&'a self, pat: P) -> Option<&'a Self>
    where
        P: Pattern<'a, Searcher: ReverseSearcher<'a>>;
}

impl StrExt for str {
    #[cfg(feature = "str_pattern_extensions")]
    fn split1_opt<'a, P>(&'a self, pat: P) -> Option<(&'a Self, &'a Self)>
    where
        P: Pattern<'a>,
    {
        self.split_once(pat)
    }

    #[cfg(feature = "str_pattern_extensions")]
    fn split1<'a, P>(&'a self, pat: P) -> (&'a Self, &'a Self)
    where
        P: Pattern<'a>,
    {
        self.split_once(pat).unwrap_or((self, ""))
    }

    #[cfg(feature = "str_pattern_extensions")]
    fn trim_start_match_opt<'a, P>(&'a self, pat: P) -> Option<&'a Self>
    where
        P: Pattern<'a>,
    {
        let mut matcher = pat.into_searcher(self);
        match matcher.next() {
            SearchStep::Match(0, n) => Some(&self[n..]),
            _ => None,
        }
    }

    #[cfg(feature = "str_pattern_extensions")]
    fn trim_start_match<'a, P>(&'a self, pat: P) -> &'a Self
    where
        P: Pattern<'a>,
    {
        #[allow(deprecated)]
        self.trim_start_match_opt(pat).unwrap_or(self)
    }

    #[cfg(feature = "str_pattern_extensions")]
    fn trim_end_match_opt<'a, P>(&'a self, pat: P) -> Option<&'a Self>
    where
        P: Pattern<'a, Searcher: ReverseSearcher<'a>>,
    {
        let mut matcher = pat.into_searcher(self);
        match matcher.next_back() {
            SearchStep::Match(n, _) => Some(&self[0..n]),
            _ => None,
        }
    }

    #[cfg(feature = "str_pattern_extensions")]
    fn trim_end_match<'a, P>(&'a self, pat: P) -> &'a Self
    where
        P: Pattern<'a, Searcher: ReverseSearcher<'a>>,
    {
        #[allow(deprecated)]
        self.trim_end_match_opt(pat).unwrap_or(self)
    }
}


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

use std::borrow::Borrow;

use dupe::Dupe;

use crate::types::TEq;

/// Optimised collect iterator into Vec, which might be a Result.
///
/// If we do a standard .collect() on the iterator it will never have a good size hint,
/// as the lower bound will always be zero, so might reallocate several times.
/// We know the Vec will either be thrown away, or exactly `len`, so aim if we do allocate,
/// make sure it is at `len`. However, if the first element throws an error, we don't need
/// to allocate at all, so special case that.
fn collect_result<T, E>(mut it: impl ExactSizeIterator<Item = Result<T, E>>) -> Result<Vec<T>, E> {
    match it.next() {
        None => Ok(Vec::new()),
        Some(Err(e)) => Err(e),
        Some(Ok(x)) => {
            // +1 for the element we have already consumed
            let mut res = Vec::with_capacity(it.len() + 1);
            res.push(x);
            for x in it {
                res.push(x?);
            }
            Ok(res)
        }
    }
}

/// Extension traits on slices/[`Vec`](Vec).
pub trait SliceExt {
    type Item;

    /// A shorthand for `iter().map(f).collect::<Vec<_>>()`. For example:
    ///
    /// ```
    /// use gazebo::prelude::*;
    /// assert_eq!([1,2,3][..].map(|x| x*x), vec![1,4,9]);
    /// assert_eq!(vec![1,2,3].map(|x| x*x), vec![1,4,9]);
    /// ```
    ///
    /// Note that from Rust 1.55.0 there is a `map` method on
    /// arrays (e.g. `[T; N]`) so you'll need to explicitly convert
    /// arrays to slices with the `[..]` operation.
    fn map<'a, B, F>(&'a self, f: F) -> Vec<B>
    where
        F: FnMut(&'a Self::Item) -> B;

    /// A shorthand for `iter().map(f).collect::<Result<Vec<_>, _>>()`. For example:
    ///
    /// ```
    /// use gazebo::prelude::*;
    /// assert_eq!([1,2,3].try_map(|x| Ok(x*x)), Ok::<_, bool>(vec![1,4,9]));
    /// assert_eq!([1,2,-3].try_map(|x| if *x > 0 { Ok(x*x) } else { Err(false) }), Err(false));
    /// ```
    ///
    /// This function will be generalised to [`Try`](std::ops::Try) once it has been
    /// standardised.
    fn try_map<'a, B, E, F>(&'a self, f: F) -> Result<Vec<B>, E>
    where
        F: FnMut(&'a Self::Item) -> Result<B, E>;

    /// Take ownership of each item in the vector using `to_owned`. For example:
    ///
    /// ```
    /// use gazebo::prelude::*;
    /// let xs: &[&str] = &["hello", "world"];
    /// let ys: Vec<String> = xs.owned();
    /// ```
    fn owned<'a, T, R>(&'a self) -> Vec<R>
    where
        // Important constraints are:
        // * Self::Item == &'a T
        // * Borrow<T> == R
        Self::Item: TEq<&'a T>,
        R: Borrow<T>,
        T: ToOwned<Owned = R>,
        T: 'a,
        T: ?Sized,
    {
        self.map(|x| (*x.teq_ref()).to_owned())
    }

    /// If the size of vector is 1, returns the first element
    /// Otherwise, returns None
    /// ```
    /// use gazebo::prelude::*;
    /// assert_eq!(*vec![1].as_singleton().unwrap(), 1);
    /// assert_eq!(vec!['a', 'b', 'c'].as_singleton(), None);
    fn as_singleton(&self) -> Option<&Self::Item>;

    /// Copies the elements from `src` into `self`, analogous to `clone_from_slice` but for
    /// elements that are `dupe`.
    ///
    /// The length of `src` must be the same as `self`.
    ///
    /// If `T` implements `Copy`, it can be more performant to use [`std::slice::[T]::copy_from_slice`].
    ///
    /// ```
    /// use gazebo::prelude::*;
    ///
    /// let src = [1, 2, 3, 4];
    /// let mut dst = [0, 0];
    ///
    /// dst.dupe_from_slice(&src[2..]);
    /// assert_eq!(src, [1, 2, 3, 4]);
    /// assert_eq!(dst, [3, 4]);
    /// ```
    fn dupe_from_slice(&mut self, src: &[Self::Item])
    where
        Self::Item: Dupe;
}

impl<T> SliceExt for [T] {
    type Item = T;

    fn map<'a, B, F>(&'a self, f: F) -> Vec<B>
    where
        F: FnMut(&'a Self::Item) -> B,
    {
        self.iter().map(f).collect()
    }

    fn try_map<'a, B, E, F>(&'a self, f: F) -> Result<Vec<B>, E>
    where
        F: FnMut(&'a Self::Item) -> Result<B, E>,
    {
        collect_result(self.iter().map(f))
    }

    fn as_singleton(&self) -> Option<&T> {
        match self {
            [x] => Some(x),
            _ => None,
        }
    }

    fn dupe_from_slice(&mut self, src: &[Self::Item])
    where
        Self::Item: Dupe,
    {
        self.clone_from_slice(src)
    }
}

// TODO(bobyf) merge these traits if we can properly implement linters without requiring separate traits
/// Short hand analogous to `Iter::cloned`, where items of `&T` are converted to `T` via clone.
///
/// ```
/// use gazebo::prelude::*;
///
/// #[derive(Clone, Debug, PartialEq)]
/// struct X;
///
/// let x = [&X];
/// let y : Vec<X> = x.cloned();
///
/// assert_eq!(y, vec![X]);
///
/// let x = vec![&X];
/// let y : Vec<X> = x.cloned();
///
/// assert_eq!(y, vec![X]);
/// ```
pub trait SliceClonedExt {
    type Item;

    fn cloned(&self) -> Vec<Self::Item>;
}

impl<T> SliceClonedExt for [&T]
where
    T: Clone,
{
    type Item = T;

    fn cloned(&self) -> Vec<Self::Item> {
        self.map(|x| (*x).clone())
    }
}

/// Short hand analogous to `Iter::duped`, where items of `&T` are converted to `T` via `dupe`.
///
/// ```
/// use dupe::Dupe;
/// use gazebo::prelude::*;
///
/// #[derive(Clone, Dupe, Debug, PartialEq)]
/// struct X;
///
/// let x = [&X];
/// let y : Vec<X> = x.duped();
///
/// assert_eq!(y, vec![X]);
///
/// let x = vec![&X];
/// let y : Vec<X> = x.duped();
///
/// assert_eq!(y, vec![X]);
/// ```
pub trait SliceDupedExt {
    type Item;

    fn duped(&self) -> Vec<Self::Item>;
}

impl<T> SliceDupedExt for [&T]
where
    T: Dupe,
{
    type Item = T;

    fn duped(&self) -> Vec<Self::Item> {
        self.map(|x| (*x).dupe())
    }
}

/// Short hand analogous to `Iter::copied`, where items of `&T` are converted to `T` via `copy`.
///
/// ```
/// use gazebo::prelude::*;
///
/// #[derive(Copy, Clone, Debug, PartialEq)]
/// struct X;
///
/// let x = [&X];
/// let y : Vec<X> = x.copied();
///
/// assert_eq!(y, vec![X]);
///
/// let x = vec![&X];
/// let y : Vec<X> = x.copied();
///
/// assert_eq!(y, vec![X]);
/// ```
pub trait SliceCopiedExt {
    type Item;

    fn copied(&self) -> Vec<Self::Item>;
}

impl<T> SliceCopiedExt for [&T]
where
    T: Copy,
{
    type Item = T;

    fn copied(&self) -> Vec<Self::Item> {
        self.map(|x| **x)
    }
}

/// Extension traits on [`Vec`](Vec).
pub trait VecExt {
    type Item;

    /// A shorthand for `into_iter().map(f).collect::<Vec<_>>()`. For example:
    ///
    /// ```
    /// use gazebo::prelude::*;
    /// assert_eq!(vec![1,2,3].into_map(|x| x*x), vec![1,4,9]);
    /// ```
    fn into_map<B, F>(self, f: F) -> Vec<B>
    where
        F: FnMut(Self::Item) -> B;

    /// A shorthand for `into_iter().map(f).collect::<Result<Vec<_>, _>>()`. For example:
    ///
    /// ```
    /// use gazebo::prelude::*;
    /// assert_eq!(vec![1,2,3].into_try_map(|x| Ok(x*x)), Ok::<_, bool>(vec![1,4,9]));
    /// assert_eq!(vec![1,2,-3].into_try_map(|x| if x > 0 { Ok(x*x) } else { Err(false) }), Err(false));
    /// ```
    ///
    /// This function will be generalised to [`Try`](std::ops::Try) once it has been
    /// standardised.
    fn into_try_map<B, E, F>(self, f: F) -> Result<Vec<B>, E>
    where
        F: FnMut(Self::Item) -> Result<B, E>;
}

impl<T> VecExt for Vec<T> {
    type Item = T;

    fn into_map<B, F>(self, f: F) -> Vec<B>
    where
        F: FnMut(Self::Item) -> B,
    {
        self.into_iter().map(f).collect()
    }

    fn into_try_map<B, E, F>(self, f: F) -> Result<Vec<B>, E>
    where
        F: FnMut(Self::Item) -> Result<B, E>,
    {
        collect_result(self.into_iter().map(f))
    }
}


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

use proc_macro2::TokenStream;
use quote::quote;
use quote::quote_spanned;
use syn::spanned::Spanned;
use syn::Data;
use syn::DataStruct;
use syn::DeriveInput;
use syn::Fields;

pub fn derive_default_(input: DeriveInput) -> proc_macro::TokenStream {
    let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();

    let name = &input.ident;
    let body = default_impl(&input.data);
    let gen = quote! {
        impl #impl_generics ::std::default::Default for #name #ty_generics #where_clause {
            fn default() -> Self {
                #body
            }
        }
    };
    gen.into()
}

fn default_struct(data: &DataStruct) -> TokenStream {
    match data.fields {
        Fields::Named(ref fields) => {
            // Self {x: Default::default(), y: Default::default()}
            let xs = fields.named.iter().map(|f| {
                let name = &f.ident;
                quote_spanned! {f.span() =>
                    #name: ::std::default::Default::default()
                }
            });
            quote! {
                Self { #(#xs, )* }
            }
        }
        Fields::Unnamed(ref fields) => {
            // Self(Default::default(), Default::default())
            let xs = fields.unnamed.iter().map(|f| {
                quote_spanned! {f.span()=>
                    ::std::default::Default::default()
                }
            });
            quote! {
                Self ( #(#xs, )* )
            }
        }
        Fields::Unit => {
            // Self
            quote!(Self)
        }
    }
}

fn default_impl(data: &Data) -> TokenStream {
    match data {
        Data::Struct(data) => default_struct(data),
        Data::Enum(x) => syn::Error::new_spanned(x.enum_token, "Can't derive Default for enums")
            .into_compile_error(),
        Data::Union(x) => syn::Error::new_spanned(x.union_token, "Can't derive Default for unions")
            .into_compile_error(),
    }
}


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

//! Derivations for the [Gazebo library](https://docs.rs/gazebo/).
//! Usually you would use these derivations via exports from that library.

#[allow(unused_extern_crates)] // proc_macro is very special
extern crate proc_macro;

use syn::parse_macro_input;
use syn::DeriveInput;

mod default;
mod variant;

/// Derive the [`Default` trait](Default), but without requiring all type arguments to implement [`Default`](Default).
#[proc_macro_derive(Default_)]
pub fn derive_default_(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
    let input = parse_macro_input!(input as DeriveInput);

    default::derive_default_(input)
}

/// Derive the `VariantName` trait.
#[proc_macro_derive(VariantName)]
pub fn derive_variant_names(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
    let input = parse_macro_input!(input as DeriveInput);

    match variant::derive_variant_names(input) {
        Ok(tokens) => tokens,
        Err(err) => err.to_compile_error().into(),
    }
}

// Derive the `Variants` functions.
#[proc_macro_derive(UnpackVariants)]
pub fn derive_variants(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
    let input = parse_macro_input!(input as DeriveInput);

    match variant::derive_unpack_variants(input) {
        Ok(tokens) => tokens,
        Err(err) => err.to_compile_error().into(),
    }
}


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

use proc_macro2::Span;
use quote::quote;
use syn::spanned::Spanned;
use syn::Data;
use syn::DeriveInput;
use syn::Fields;
use syn::Ident;

pub(crate) fn derive_variant_names(input: DeriveInput) -> syn::Result<proc_macro::TokenStream> {
    if let Data::Enum(data_enum) = input.data {
        let mut variant_body = Vec::new();
        for variant in data_enum.variants {
            let variant_name = &variant.ident;
            let patterns = match variant.fields {
                Fields::Unit => quote! {},
                Fields::Named(_) => quote! { {..} },
                Fields::Unnamed(_) => quote! { (..) },
            };
            let variant_name_str = variant_name.to_string();
            variant_body.push(quote! {
                Self::#variant_name #patterns => #variant_name_str
            });
        }

        let name = &input.ident;
        let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();

        let gen = quote! {
            impl #impl_generics gazebo::variants::VariantName for #name #ty_generics #where_clause {
                fn variant_name(&self) -> &'static str {
                    match self {
                        #(#variant_body,)*
                    }
                }
            }
        };

        Ok(gen.into())
    } else {
        Err(syn::Error::new(
            input.span(),
            "Can only derive variant name on enums",
        ))
    }
}

pub(crate) fn derive_unpack_variants(input: DeriveInput) -> syn::Result<proc_macro::TokenStream> {
    if let Data::Enum(data_enum) = input.data {
        let mut variant_fns = Vec::new();
        for variant in data_enum.variants {
            let variant_name = &variant.ident;

            let mut count = 0;
            let mut patterns = Vec::new();
            let mut inner_type = Vec::new();

            for field in variant.fields.iter() {
                patterns.push(field.ident.clone().map_or_else(
                    || {
                        let id = Ident::new(&format!("_v{}", count), Span::call_site());
                        count += 1;
                        id
                    },
                    |f| f,
                ));

                inner_type.push(&field.ty);
            }

            let (patterned_out, borrowed_inner_type, owned_inner_type) =
                if variant.fields.len() == 1 {
                    let patterned_out = quote! { #(#patterns)* };
                    let borrowed_inner_type = quote! { #(&'__gazebo_variant_a #inner_type)*  };
                    let owned_inner_type = quote! { #(#inner_type)*  };

                    (patterned_out, borrowed_inner_type, owned_inner_type)
                } else {
                    let patterned_out = quote! { (#(#patterns,)*) };
                    let borrowed_inner_type = quote! { (#(&'__gazebo_variant_a #inner_type,)*) };
                    let owned_inner_type = quote! { (#(#inner_type,)*) };

                    (patterned_out, borrowed_inner_type, owned_inner_type)
                };
            let patterns = match variant.fields {
                Fields::Named(_) => quote! { { #(#patterns,)*} },
                Fields::Unnamed(_) => quote! { ( #(#patterns,)* ) },
                Fields::Unit => quote!(),
            };

            let variant_unpack_fn_name = Ident::new(
                &format!("unpack_{}", to_snake_case(&variant_name.to_string())),
                Span::call_site(),
            );

            let variant_into_fn_name = Ident::new(
                &format!("into_{}", to_snake_case(&variant_name.to_string())),
                Span::call_site(),
            );

            variant_fns.push(quote! {
                pub fn #variant_unpack_fn_name<'__gazebo_variant_a>(&'__gazebo_variant_a self) -> Option<#borrowed_inner_type> {
                    match self {
                       Self::#variant_name #patterns => Some(#patterned_out),
                       _ => None
                    }
                }

                pub fn #variant_into_fn_name(self) -> Option<#owned_inner_type> {
                    match self {
                       Self::#variant_name #patterns => Some(#patterned_out),
                       _ => None
                    }
                }
            });
        }

        let name = &input.ident;
        let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();

        let gen = quote! {
            impl #impl_generics #name #ty_generics #where_clause {
                #(#variant_fns)*
            }
        };

        Ok(gen.into())
    } else {
        Err(syn::Error::new(
            input.span(),
            "Can only derive variant name on enums",
        ))
    }
}

fn to_snake_case(s: &str) -> String {
    let mut out = String::new();
    let mut is_first = true;
    for c in s.chars() {
        if c.is_ascii_uppercase() {
            if !is_first {
                out.push('_');
            }
            out.push(c.to_ascii_lowercase());
        } else {
            out.push(c);
        }
        is_first = false;
    }

    out
}


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

use std::fmt;

use allocative::Allocative;
use anyhow::Context;
use futures_intrusive::sync::SharedSemaphore;
use futures_intrusive::sync::SharedSemaphoreReleaser;

use crate::NamedSemaphores;

const SINGLE_RUN: usize = 1;

/// This class is intended to represent the resources required by each test. This is then used to
/// map onto resources available on the machine where the tests are run on in order to not saturate the
/// machine and adversely impact testrunning performance and reliability.
/// Testpilot Classic uses the concept of machine cores to manage resources. Testpilot has two types of
/// tests: Normal (1 core) and Heavy (4 cores) where this would run on a machine with typically 24 cores
/// (managed through a semaphore with size 24). Since Testpilot was introduced we have moved to run
/// on Sandcastle machines with 56 cores so we want to move away from the core-analogy and instead use
/// the term "permits" to describe the limited resources available on each machine.
/// More long term we want improve this to also take into account memory usage, cpu usage etc.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Allocative)]
pub enum WeightClass {
    /// Tests can require any number of permits and this can be used to mimic resource utilization like
    /// memory or cpu. For now, we map the Testpilot behaviour as Normal->Permits(1) and Heavy->Permits(4).
    Permits(usize),
    /// A percentage of available resources.
    Percentage(WeightPercentage),
}

impl fmt::Display for WeightClass {
    fn fmt(&self, w: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Self::Permits(p) => write!(w, "{}", p),
            Self::Percentage(p) => write!(w, "{}%", p.into_value()),
        }
    }
}

#[derive(Debug, Copy, Clone, PartialEq, Eq, Allocative)]
pub struct WeightPercentage {
    value: u8, // Between 0 and 100
}

impl WeightPercentage {
    pub fn try_new<T, E>(value: T) -> anyhow::Result<Self>
    where
        u8: TryFrom<T, Error = E>,
        E: Into<anyhow::Error>,
    {
        let value = u8::try_from(value)
            .map_err(|e| e.into())
            .context("WeightPercentage value must convert to u8")?;

        if value > 100 {
            return Err(anyhow::anyhow!(
                "WeightPercentage value cannot exceed 100: {}",
                value
            ));
        }

        Ok(Self { value })
    }

    pub fn into_value(self) -> u8 {
        self.value
    }
}

/// Some commands require that we only run one instance of this binary (using an identifier)
/// to check for other instances of the same binary.
/// Some commands required the full host to run, others just dont care.
/// This enum encapsulates all the different scenarios.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum HostSharingRequirements {
    /// Needs exclusive access to the host. No other processes should run.
    ExclusiveAccess,
    /// Can share with other processes, but not with others requiring the same token
    OnePerToken(String, WeightClass),
    /// Run with any other processes within reasonable limits.
    Shared(WeightClass),
}

impl Default for HostSharingRequirements {
    fn default() -> HostSharingRequirements {
        HostSharingRequirements::Shared(WeightClass::Permits(1))
    }
}

/// A guard for all permits and resources acquired for a HostSharingBroker.acquire request.
/// Keeps the data structures received from semaphores after acquiring.
/// Semaphores are held until this struct is dropped.
pub struct HostSharingGuard {
    _run_guard: SharedSemaphoreReleaser,
    _name_guard: Option<SharedSemaphoreReleaser>,
}

/// Used to ensure that host resources are properly reserved before executing a command spec.
pub struct HostSharingBroker {
    permits: SharedSemaphore,
    num_machine_permits: usize,
    named_semaphores: NamedSemaphores,
}

impl HostSharingBroker {
    // If a test requires Permits(4) permits but the machine only has 3 permits then we cap the
    // test's required permits to 3. Otherwise the test would never be allowed to run.
    pub fn requested_permits(&self, weight_class: &WeightClass) -> usize {
        match weight_class {
            WeightClass::Permits(required_permits) => {
                self.num_machine_permits.min(*required_permits)
            }
            WeightClass::Percentage(percentage) => {
                let percentage: usize = percentage.into_value().into();
                (self.num_machine_permits * percentage).div_ceil(100)
            }
        }
    }

    pub fn new(host_sharing_strategy: HostSharingStrategy, num_machine_permits: usize) -> Self {
        let permits = match host_sharing_strategy {
            HostSharingStrategy::Fifo => SharedSemaphore::new(true, num_machine_permits),
            HostSharingStrategy::SmallerTasksFirst => {
                SharedSemaphore::new(false, num_machine_permits)
            }
        };

        Self {
            permits,
            num_machine_permits,
            named_semaphores: NamedSemaphores::new(),
        }
    }

    pub fn num_machine_permits(&self) -> usize {
        self.num_machine_permits
    }

    pub async fn acquire(
        &self,
        host_sharing_requirements: &HostSharingRequirements,
    ) -> HostSharingGuard {
        match host_sharing_requirements {
            HostSharingRequirements::Shared(weight_class) => {
                let permits = self.requested_permits(weight_class);
                let _run_guard = self.permits.acquire(permits).await;
                HostSharingGuard {
                    _run_guard,
                    _name_guard: None,
                }
            }
            HostSharingRequirements::ExclusiveAccess => {
                let _run_guard = self.permits.acquire(self.num_machine_permits).await;
                HostSharingGuard {
                    _run_guard,
                    _name_guard: None,
                }
            }
            HostSharingRequirements::OnePerToken(identifier, weight_class) => {
                // Ensure that there is only one active run per identifier.
                // Acquire the identifier semaphore first, then acquire the permits needed to actually run.
                // This is so that no permits (which map to system resources / cores) are held while waiting
                // for the previous run on this identifier to finish.
                let run_semaphore = self.named_semaphores.get(identifier);
                let _name_guard = Some(run_semaphore.acquire(SINGLE_RUN).await);
                let permits = self.requested_permits(weight_class);
                let _run_guard = self.permits.acquire(permits).await;
                HostSharingGuard {
                    _run_guard,
                    _name_guard,
                }
            }
        }
    }
}

/// Determines whether a fair or unfair semaphore is used to manage host sharing
pub enum HostSharingStrategy {
    SmallerTasksFirst,
    Fifo,
}

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

    #[test]
    // if we only have 2 machine permits then even a test requiring 4 permits will be capped to only require 2 permits
    // (otherwise it would not run)
    fn test_heavyweight_capped_to_machine_permits() {
        let broker = HostSharingBroker::new(HostSharingStrategy::SmallerTasksFirst, 2);

        let permits = broker.requested_permits(&WeightClass::Permits(4));
        assert_eq!(2, permits);
    }

    #[test]
    fn test_percentage() {
        let broker = HostSharingBroker::new(HostSharingStrategy::SmallerTasksFirst, 10);

        assert_eq!(
            broker.requested_permits(&WeightClass::Percentage(WeightPercentage { value: 0 })),
            0,
        );

        assert_eq!(
            broker.requested_permits(&WeightClass::Percentage(WeightPercentage { value: 40 })),
            4,
        );

        // This rounds up.
        assert_eq!(
            broker.requested_permits(&WeightClass::Percentage(WeightPercentage { value: 15 })),
            2,
        );

        assert_eq!(
            broker.requested_permits(&WeightClass::Percentage(WeightPercentage { value: 99 })),
            10,
        );

        assert_eq!(
            broker.requested_permits(&WeightClass::Percentage(WeightPercentage { value: 100 })),
            10,
        );
    }
}


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

#![feature(int_roundings)]
#![deny(unused_crate_dependencies)]
mod named_semaphores;
pub use named_semaphores::NamedSemaphores;

pub mod host_sharing;
pub use crate::host_sharing::HostSharingBroker;
pub use crate::host_sharing::HostSharingRequirements;
pub use crate::host_sharing::HostSharingStrategy;
pub use crate::host_sharing::WeightClass;
pub use crate::host_sharing::WeightPercentage;


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

use dashmap::DashMap;
use futures_intrusive::sync::SharedSemaphore;

const SINGLE_WORKER: usize = 1;

#[derive(Default)]
pub struct NamedSemaphores {
    buckets: DashMap<String, SharedSemaphore>,
}

impl NamedSemaphores {
    pub fn new() -> NamedSemaphores {
        Default::default()
    }

    pub fn get(&self, name: &str) -> SharedSemaphore {
        if let Some(bucket_semaphore) = self.buckets.get(name) {
            bucket_semaphore.clone()
        } else {
            // Fairness on this semaphore doesn't control the order in which waiters are woken up, but
            // simply whether we delay wakeups to wait for waiters that want > 1 permit. Since we only
            // ever request a single permit here, the fairness doesn't matter.
            //
            // Since a fair semaphore will only ever wake a single waiter, that code path is a bit
            // faster and doesn't require any additional looping. Therefore despite the fact that a
            // fair and unfair semaphore would have the same end result, we use a fair semaphore
            // here as a small performance optimization.
            let bucket_semaphore = SharedSemaphore::new(true, SINGLE_WORKER);
            self.buckets
                .insert(name.to_owned(), bucket_semaphore.clone());
            bucket_semaphore
        }
    }
}

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

    #[test]
    fn test_named_semaphore_works() {
        let named_semaphore = NamedSemaphores::new();
        let identifier = "testing".to_owned();
        {
            let first_permit = named_semaphore.get(&identifier);
            assert!(first_permit.try_acquire(1).is_some());
        }
        {
            let second_permit = named_semaphore.get(&identifier);
            assert!(second_permit.try_acquire(1).is_some());
        }
    }

    #[test]
    fn test_multiple_locks_fail() {
        let named_semaphore = NamedSemaphores::new();
        let identifier = "testing".to_owned();
        let first_permit = named_semaphore.get(&identifier);
        let first_permit_acquire = first_permit.try_acquire(1);
        assert!(first_permit_acquire.is_some());
        let second_permit = named_semaphore.get(&identifier);
        assert!(second_permit.try_acquire(1).is_none());
    }
}


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

use std::collections::BTreeMap;
use std::collections::BTreeSet;
use std::collections::HashMap;
use std::io;
use std::path::Path;
use std::path::PathBuf;
use std::process::Command;
use std::process::ExitStatus;
use std::process::Output;
use std::process::Stdio;

use anyhow::Context;
use serde::Deserialize;
use tracing::debug;
use tracing::enabled;
use tracing::info;
use tracing::instrument;
use tracing::trace;
use tracing::warn;
use tracing::Level;

use crate::json_project::Edition;
use crate::json_project::JsonProject;
use crate::json_project::Sysroot;
use crate::target::AliasedTargetInfo;
use crate::target::Kind;
use crate::target::MacroOutput;
use crate::target::Target;
use crate::target::TargetInfo;
use crate::target::TargetInfoEntry;
use crate::Crate;
use crate::Dep;

pub fn to_json_project(
    sysroot: Sysroot,
    targets: Vec<Target>,
    target_map: BTreeMap<Target, TargetInfo>,
    aliases: BTreeMap<Target, AliasedTargetInfo>,
    proc_macros: BTreeMap<Target, MacroOutput>,
    relative_paths: bool,
) -> Result<JsonProject, anyhow::Error> {
    let targets: BTreeSet<_> = targets.into_iter().collect();
    let target_index = merge_unit_test_targets(target_map);
    let project_root = Buck.resolve_project_root()?;

    let mut crates: Vec<Crate> = Vec::with_capacity(target_index.len());
    for (target, TargetInfoEntry { info, index: _ }) in &target_index {
        let mut deps = resolve_dependencies_aliases(info, &target_index, &aliases, &proc_macros);
        resolve_renamed_dependencies(info, &target_index, &mut deps);

        let edition = match &info.edition {
            Some(edition) => edition.clone(),
            None => Edition::Edition2021,
        };

        // we need to take the existing features and prefix `feature=`
        // before passing it to rust-analyzer via `rust-project.json`.
        let mut cfg = info
            .clone()
            .features
            .into_iter()
            .map(|f| format!("feature=\"{f}\""))
            .collect::<Vec<String>>();

        // Include "test" cfg so rust-analyzer picks up #[cfg(test)] code.
        cfg.push("test".to_owned());

        // the mapping here is inverted, which means we need to search through the keys for the Target.
        // thankfully, most projects don't have to many proc macros, which means the size of this list
        // remains in the two digit space.
        let mut proc_macro_dylib_path = proc_macros
            .values()
            .find(|macro_output| macro_output.actual == *target)
            .map(|macro_output| macro_output.dylib.clone());
        if let Some(ref dylib) = proc_macro_dylib_path {
            trace!(?target, ?dylib, "target is a proc macro");
        }

        // We don't need to push the source folder as rust-analyzer by default will use the root-module parent().
        // info.root_module() will output either the fbcode source file or the symlinked one based on if it's a mapped source or not
        let mut root_module = info.root_module();

        if relative_paths {
            proc_macro_dylib_path = proc_macro_dylib_path.map(|p| relative_to(&p, &project_root));
            root_module = relative_to(&root_module, &project_root);
        }

        let crate_info = Crate {
            display_name: Some(info.name.clone()),
            root_module,
            edition,
            deps,
            is_workspace_member: targets.contains(target),
            source: None,
            cfg,
            target: None,
            env: BTreeMap::new(),
            is_proc_macro: info.proc_macro.unwrap_or(false),
            proc_macro_dylib_path,
        };
        crates.push(crate_info);
    }

    let jp = JsonProject {
        sysroot,
        crates,
        // needed to ignore the generated `rust-project.json` in diffs, but including the actual
        // string will mark this file as generated
        generated: "\x40generated",
    };

    Ok(jp)
}

/// If `path` starts with `base`, drop the prefix.
pub fn relative_to(path: &Path, base: &Path) -> PathBuf {
    match path.strip_prefix(base) {
        Ok(rel_path) => rel_path,
        Err(_) => path,
    }
    .to_owned()
}

fn resolve_dependencies_aliases(
    info: &TargetInfo,
    target_index: &BTreeMap<Target, TargetInfoEntry>,
    aliases: &BTreeMap<Target, AliasedTargetInfo>,
    proc_macros: &BTreeMap<Target, MacroOutput>,
) -> Vec<Dep> {
    let mut deps = vec![];
    for dependency_target in &info.deps {
        let dependency_target = match aliases.get(dependency_target) {
            Some(actual) => &actual.actual,
            None => {
                // we fall back to check the proc macros for aliases
                // (these should exist in the aliases map, but they don't. yolo.)
                match proc_macros.get(dependency_target) {
                    Some(MacroOutput { actual, .. }) => actual,
                    None => dependency_target,
                }
            }
        };

        if let Some(entry) = target_index.get(dependency_target) {
            trace!(?dependency_target, "present in target_index");
            let dep = Dep {
                crate_index: entry.index,
                name: entry.info.crate_name(),
            };
            deps.push(dep);
        } else {
            trace!(?dependency_target, "not present in target_index");
        }
    }

    deps
}

fn resolve_renamed_dependencies(
    info: &TargetInfo,
    target_index: &BTreeMap<Target, TargetInfoEntry>,
    deps: &mut Vec<Dep>,
) {
    // we handled named_deps when constructing the dependency, as rust-analyzer cares about the
    // the crate name for correct resolution. `named_deps` are distinct from `deps` in buck2 and
    // are not currently unified into a `buck.direct_dependencies` or `$deps` in buck2.
    // TODO: once https://fb.workplace.com/groups/buck2users/posts/3137264549863238/?comment_id=3137265756529784
    // is resolved, switch to `$deps`.
    for (renamed_crate, dependency_target) in &info.named_deps {
        if let Some(entry) = target_index.get(dependency_target) {
            trace!(old_name = ?entry.info.crate_name(), new_name = ?renamed_crate, "renamed crate");
            // if the renamed dependency was encountered before, rename the existing `Dep` rather
            // than create a new one with a new name but the same index. While this duplication doesn't
            // seem to have any noticeable impact in limited testing, the behavior will be closer to
            // that of Rusty and Cargo.
            //
            // However, if the renamed dependency wasn't encountered before, we create a new `Dep` with
            // the new name.
            //
            // The primary invariant that is being upheld is that each index should
            // have one associated name.
            match deps.iter_mut().find(|dep| dep.crate_index == entry.index) {
                Some(dep) => dep.name = renamed_crate.to_string(),
                None => {
                    let dep = Dep {
                        crate_index: entry.index,
                        name: renamed_crate.to_string(),
                    };
                    deps.push(dep);
                }
            };
        }
    }
}

fn merge_unit_test_targets(
    target_map: BTreeMap<Target, TargetInfo>,
) -> BTreeMap<Target, TargetInfoEntry> {
    let mut target_index = BTreeMap::new();

    let (tests, mut targets): (BTreeMap<Target, TargetInfo>, BTreeMap<Target, TargetInfo>) =
        target_map
            .into_iter()
            .partition(|(_, info)| info.kind == Kind::Test);

    let (generated_unit_tests, standalone_tests): (
        BTreeMap<Target, TargetInfo>,
        BTreeMap<Target, TargetInfo>,
    ) = tests.into_iter().partition(|(target, _)| {
        targets
            .iter()
            .any(|(_, value)| value.tests.contains(target))
    });

    targets.extend(standalone_tests);

    for (index, (target, mut info)) in targets.into_iter().enumerate() {
        trace!(?target, ?info, index, "adding dependency");

        // merge the `-unittest` target with the parent target.
        for test_dep in &info.tests {
            if let Some(test_info) = generated_unit_tests.get(test_dep) {
                for test_dep in &test_info.deps {
                    if !info.deps.contains(test_dep) {
                        info.deps.push(test_dep.clone())
                    }
                }
            }
        }
        target_index.insert(target.to_owned(), TargetInfoEntry { index, info });
    }
    target_index
}

#[derive(Debug)]
pub struct Buck;

impl Buck {
    pub fn command(&self) -> Command {
        Command::new("buck2")
    }

    /// Return the absolute path of the current Buck project root.
    pub fn resolve_project_root(&self) -> Result<PathBuf, anyhow::Error> {
        let mut command = self.command();
        command.args(["root", "--kind=project"]);

        let mut stdout = utf8_output(command.output(), &command)?;
        truncate_line_ending(&mut stdout);

        if enabled!(Level::TRACE) {
            trace!(%stdout, "got root from buck");
        }

        Ok(stdout.into())
    }

    pub fn resolve_sysroot_src(&self) -> Result<PathBuf, anyhow::Error> {
        let mut command = self.command();
        command.args(["audit", "config", "--json", "--", "rust.sysroot_src_path"]);
        command
            .stderr(Stdio::null())
            .stdout(Stdio::piped())
            .stderr(Stdio::piped());

        // the `spawn()` here is load-bearing to allow spawning
        // this command concurrently with rustc.
        let child = command.spawn().context("Unable to spawn command")?;

        #[derive(Deserialize)]
        struct BuckConfig {
            #[serde(rename = "rust.sysroot_src_path")]
            sysroot_src_path: PathBuf,
        }
        let cfg: BuckConfig = deserialize_output(child.wait_with_output(), &command)?;
        // the `library` path component needs to be appended to the `sysroot_src_path`
        // so that rust-analyzer will be able to find standard library sources.
        let cfg = cfg.sysroot_src_path.join("library");
        Ok(cfg)
    }

    /// Expands a Buck target expression.
    ///
    /// Since `rust-project` accepts Buck target experssions like '//common/rust/tools/rust-project/...',
    /// it is necessary to expand the target expression to query the *actual* targets.
    #[instrument(skip_all)]
    pub fn expand_targets(&self, targets: &[Target]) -> Result<Vec<Target>, anyhow::Error> {
        let mut command = self.command();
        command.args([
            "bxl",
            "prelude//rust/rust-analyzer/resolve_deps.bxl:expand_targets",
            "--",
            "--targets",
        ]);
        command.args(targets);
        tracing::info!(?targets);
        let raw = deserialize_output(command.output(), &command)?;
        if enabled!(Level::TRACE) {
            for target in &raw {
                trace!(%target, "Expanded target from buck");
            }
        }
        Ok(raw)
    }

    #[instrument(skip_all)]
    pub fn resolve_deps(
        &self,
        targets: &[Target],
    ) -> Result<BTreeMap<Target, TargetInfo>, anyhow::Error> {
        let mut command = self.command();
        command.args([
            "bxl",
            "prelude//rust/rust-analyzer/resolve_deps.bxl:query",
            "--",
            "--targets",
        ]);
        command.args(targets);

        if targets.len() <= 10 {
            // printing out 10 targets is pretty reasonable information for the user
            info!(
                targets_num = targets.len(),
                ?targets,
                "resolving dependencies"
            );
        } else {
            // after 10 targets, however, things tend to get a bit unwieldy.
            info!(targets_num = targets.len(), "resolving dependencies");
            debug!(?targets);
        }
        let raw = deserialize_output(command.output(), &command)?;

        if enabled!(Level::TRACE) {
            for (target, info) in &raw {
                trace!(%target, ?info, "parsed target from buck");
            }
        }
        Ok(raw)
    }

    #[instrument(skip_all)]
    pub fn query_proc_macros(
        &self,
        targets: &[Target],
    ) -> Result<BTreeMap<Target, MacroOutput>, anyhow::Error> {
        let mut command = self.command();

        command.args([
            "bxl",
            "prelude//rust/rust-analyzer/resolve_deps.bxl:expand_proc_macros",
            "--",
            "--targets",
        ]);
        command.args(targets);

        info!("building proc macros");
        let raw: BTreeMap<Target, MacroOutput> = deserialize_output(command.output(), &command)?;

        Ok(raw)
    }

    #[instrument(skip_all)]
    pub fn query_aliased_libraries(
        &self,
        targets: &[Target],
    ) -> Result<BTreeMap<Target, AliasedTargetInfo>, anyhow::Error> {
        let mut command = self.command();

        // Fetch all aliases used by transitive deps. This is so we
        // can translate an apparent dependency of e.g.
        // fbsource//third-party/rust:once_cell to the actual target
        // name of fbsource//third-party/rust:once_cell-1.15. This
        // query also fetches non-Rust aliases, but they shouldn't
        // hurt anything.
        command.args([
            "cquery",
            "--output-all-attributes",
            "kind('^alias$', deps(%Ss))",
        ]);
        command.args(targets);

        info!("resolving aliased libraries");
        let raw: BTreeMap<Target, AliasedTargetInfo> =
            deserialize_output(command.output(), &command)?;

        if enabled!(Level::TRACE) {
            for (target, info) in &raw {
                trace!(%target, ?info, "Parsed target from buck");
            }
        }
        Ok(raw)
    }

    #[instrument(skip_all)]
    pub fn query_owner(
        &self,
        files: Vec<PathBuf>,
    ) -> Result<HashMap<PathBuf, Vec<Target>>, anyhow::Error> {
        let mut command = self.command();

        command.args(["uquery", "--json", "owner(%s)", "--"]);
        command.args(&files);

        info!(?files, "Querying buck to determine owner");
        let out = deserialize_output(command.output(), &command)?;
        Ok(out)
    }
}

pub fn utf8_output(output: io::Result<Output>, command: &Command) -> Result<String, anyhow::Error> {
    match output {
        Ok(Output {
            stdout,
            stderr,
            status,
        }) if status.success() => String::from_utf8(stdout)
            .or_else(|err| {
                let context = cmd_err(command, status, &stderr);
                Err(err).context(context)
            })
            .context("command returned non-utf8 output"),
        Ok(Output {
            stdout: _,
            stderr,
            status,
        }) => Err(cmd_err(command, status, &stderr))
            .with_context(|| format!("command ended with {}", status)),
        Err(err) => Err(err)
            .with_context(|| format!("command `{:?}`", command))
            .context("failed to execute command"),
    }
}

pub fn deserialize_output<T>(
    output: io::Result<Output>,
    command: &Command,
) -> Result<T, anyhow::Error>
where
    T: for<'a> Deserialize<'a>,
{
    match output {
        Ok(Output {
            stdout,
            stderr,
            status,
        }) => {
            tracing::debug!(?command, "parsing command output");
            serde_json::from_slice(&stdout)
                .with_context(|| cmd_err(command, status, &stderr))
                .context("failed to deserialize command output")
        }
        Err(err) => Err(err)
            .with_context(|| format!("command `{:?}`", command))
            .context("failed to execute command"),
    }
}

fn cmd_err(command: &Command, status: ExitStatus, stderr: &[u8]) -> anyhow::Error {
    anyhow::anyhow!(
        "command `{:?}` (exit code: {})\nstderr:\n{}",
        command,
        status,
        String::from_utf8_lossy(stderr),
    )
}

/// Trim a trailing new line from `String`.
/// Useful when trimming command output.
pub fn truncate_line_ending(s: &mut String) {
    if let Some(x) = s.strip_suffix("\r\n").or_else(|| s.strip_suffix('\n')) {
        s.truncate(x.len());
    }
}


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

//! A build-system independent project structure defined and used by rust-analyzer.
//!
//! While this format is provisional and subject to change, `rust-project` and Rusty
//! take a necessary dependency on these definitions to provide IDE functionality to
//! buck-based projects. For additional details, see rust-analyzer's [documentation].
//!
//! [documentation]: https://rust-analyzer.github.io/manual.html#non-cargo-based-projects

use std::collections::BTreeMap;
use std::collections::BTreeSet;
use std::path::PathBuf;

use serde::Deserialize;
use serde::Serialize;

#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
pub struct JsonProject {
    #[serde(flatten)]
    pub sysroot: Sysroot,

    /// The set of crates comprising the project.
    ///
    /// Must include all transitive dependencies as well as sysroot crate (libstd,
    /// libcore, etc.).
    pub crates: Vec<Crate>,
    pub generated: &'static str,
}

#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
pub struct Crate {
    /// Optional crate name used for display purposes; has no semantic significance.
    pub display_name: Option<String>,
    /// The path to the root module of the crate.
    pub root_module: PathBuf,
    pub edition: Edition,
    pub deps: Vec<Dep>,
    /// Should this crate be treated as a member of
    /// current "workspace".
    ///
    /// By default, inferred from the `root_module`
    /// (members are the crates which reside inside
    /// the directory opened in the editor).
    ///
    /// Set this to `false` for things like standard
    /// library and 3rd party crates to enable
    /// performance optimizations (rust-analyzer
    /// assumes that non-member crates don't change).
    pub is_workspace_member: bool,
    /// Optionally specify the (super)set of `.rs`
    /// files comprising this crate.
    ///
    /// By default, rust-analyzer assumes that only
    /// files under `root_module.parent` can belong
    /// to a crate. `include_dirs` are included
    /// recursively, unless a subdirectory is in
    /// `exclude_dirs`.
    ///
    /// Different crates can share the same `source`.
    ///
    /// If two crates share an `.rs` file in common,
    /// they *must* have the same `source`.
    /// rust-analyzer assumes that files from one
    /// source can't refer to files in another source.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub source: Option<Source>,
    /// The set of cfgs activated for a given crate.
    ///
    /// With how fb imports crates into fbsource/third-party,
    /// the answer is "all of them".
    pub cfg: Vec<String>,
    /// The target triple for a given crate.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub target: Option<String>,
    /// Environment for the crate, often used by `env!`.
    pub env: BTreeMap<String, String>,
    /// Whether the crate is a proc-macro crate/
    pub is_proc_macro: bool,
    /// For proc-macro crates, path to compiled
    /// proc-macro (.so, .dylib, or .dll. depends on the platform.)
    #[serde(skip_serializing_if = "Option::is_none")]
    pub proc_macro_dylib_path: Option<PathBuf>,
}

#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
#[serde(rename = "edition")]
pub enum Edition {
    #[serde(rename = "2015")]
    Edition2015,
    #[serde(rename = "2018")]
    Edition2018,
    #[serde(rename = "2021")]
    Edition2021,
}

/// An optional set of Rust files that comprise the crate.
///
/// By default, rust-analyzer assumes that only files under
/// `Crate::root_module` can belong to a crate. `include_dirs`
/// are included recursively, unless a subdirectory is
/// specified in `include_dirs`.
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Default)]
pub struct Source {
    pub include_dirs: BTreeSet<PathBuf>,
    pub exclude_dirs: BTreeSet<PathBuf>,
}

#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
pub struct Dep {
    #[serde(rename = "crate")]
    pub crate_index: usize,
    pub name: String,
}

/// Sysroot paths. These are documented in the rust-analyzer manual:
///
/// <https://rust-analyzer.github.io/manual.html#non-cargo-based-projects>
///
/// rust-analyzer treats both paths as optional, but we always provide sysroot.
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
pub struct Sysroot {
    /// Path to the directory of the sysroot; this is a superset of `sysroot_src`.
    ///
    /// This path provides rust-analyzer both the *source code* of libraries
    /// like `std` and `core` and binaries like `rust-analyzer-proc-macro-srv`,
    /// which enable rust-analyzer to expand procedural macros.
    ///
    /// For example, a `sysroot` is `~/fbsource/fbcode/third-party-buck/platform010/build/rust/`.
    ///
    /// `rust-analyzer` relies on an external binary to expand procedural
    /// macros and the source code location can be predictably inferred.
    /// Assuming the example sysroot above, the source code would be located in
    /// `/lib/rustlib/src/rust/`.
    pub sysroot: PathBuf,
    /// Legacy sysroot config containing only the source code of libraries such
    /// as `std` and core`.
    ///
    /// Inside Meta, this is necessary on non-Linux platforms since the sources
    /// are packaged seperately from binaries such as `rust-analyzer-proc-macro-srv`.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub sysroot_src: Option<PathBuf>,
}


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

mod buck;
mod cli;
mod json_project;
mod sysroot;
mod target;

use std::io;
use std::io::IsTerminal as _;
use std::path::PathBuf;

use clap::Parser;
use clap::Subcommand;
use tracing_subscriber::filter::LevelFilter;
use tracing_subscriber::layer::SubscriberExt;
use tracing_subscriber::EnvFilter;

use crate::cli::ProjectKind;
use crate::json_project::Crate;
use crate::json_project::Dep;

#[derive(Parser, Debug, PartialEq)]
struct Opt {
    #[clap(subcommand)]
    command: Command,
}

#[derive(Subcommand, Debug, PartialEq)]
enum Command {
    /// Create a new Rust project
    New {
        /// Name of the project being created.
        name: String,
        /// Kinds of Rust projects that can be created
        #[clap(long, value_enum, default_value = "binary")]
        kind: ProjectKind,

        /// Path to create new crate at. The new directory will be created as a
        /// subdirectory.
        path: Option<PathBuf>,
    },
    /// Convert buck's build to a format that rust-analyzer can consume.
    Develop {
        /// Buck targets to include in rust-project.json.
        #[clap(required = true, conflicts_with = "files", multiple_values = true)]
        targets: Vec<String>,

        /// Path of the file being developed.
        ///
        /// Used to discover the owning set of targets.
        #[clap(required = true, last = true, multiple_values = true)]
        files: Vec<PathBuf>,

        /// Where to write the generated `rust-project.json`.
        ///
        /// If not provided, rust-project will write in the current working directory.
        #[clap(short = 'o', long, value_hint = clap::ValueHint::DirPath, default_value = "rust-project.json")]
        out: PathBuf,

        /// Writes the generated `rust-project.json` to stdout.
        #[clap(long = "stdout", conflicts_with = "out")]
        stdout: bool,

        /// Use a `rustup`-managed sysroot instead of a `.buckconfig`-managed sysroot.
        ///
        /// This option requires the presence of `rustc` in the `$PATH`, as rust-project
        /// will run `rustc --print sysroot` and ignore any other `sysroot` configuration.
        #[clap(conflicts_with = "sysroot", default_value = "false")]
        prefer_rustup_managed_toolchain: bool,

        /// The directory containing the Rust source code, including std.
        /// Default value is determined based on platform.
        #[clap(short = 's', long)]
        sysroot: Option<PathBuf>,

        /// Pretty-print generated `rust-project.json` file.
        #[clap(short, long)]
        pretty: bool,

        /// Use paths relative to the project root in `rust-project.json`.
        #[clap(long, hide = true)]
        relative_paths: bool,
    },
}

fn main() -> anyhow::Result<()> {
    let filter = EnvFilter::builder()
        .with_default_directive(LevelFilter::INFO.into())
        .from_env()?;
    let subscriber = tracing_subscriber::registry()
        .with(
            tracing_subscriber::fmt::layer()
                .with_ansi(io::stderr().is_terminal())
                .with_writer(io::stderr),
        )
        .with(filter);
    tracing::subscriber::set_global_default(subscriber)?;

    let cli = Opt::parse();

    match cli.command {
        Command::New { name, kind, path } => cli::New { name, kind, path }.run(),
        c @ Command::Develop { .. } => cli::Develop::from(c).run(),
    }
}


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

use std::path::Path;
use std::path::PathBuf;
use std::process::Command;
use std::process::Stdio;

use anyhow::Context;
use tracing::instrument;

use crate::buck::relative_to;
use crate::buck::truncate_line_ending;
use crate::buck::utf8_output;
use crate::buck::Buck;
use crate::json_project::Sysroot;

pub enum SysrootConfig {
    Sysroot(PathBuf),
    BuckConfig,
    Rustup,
}

/// Choose sysroot and sysroot_src based on platform.
///
/// `sysroot` is the directory that contains std crates:
/// <https://doc.rust-lang.org/rustc/command-line-arguments.html#--sysroot-override-the-system-root>
/// and also contains libexec helpers such as rust-analyzer-proc-macro-srv.
///
/// `sysroot_src` is the directory that contains the source to std crates:
/// <https://rust-analyzer.github.io/manual.html#non-cargo-based-projects>
#[instrument(ret)]
pub fn resolve_buckconfig_sysroot(
    project_root: &Path,
    relative_paths: bool,
) -> Result<Sysroot, anyhow::Error> {
    let buck = Buck;

    if cfg!(target_os = "linux") {
        let base: PathBuf = if relative_paths {
            PathBuf::from("")
        } else {
            project_root.into()
        };

        let sysroot_src = buck.resolve_sysroot_src()?;
        let sysroot_src = if relative_paths {
            sysroot_src
        } else {
            project_root.join(sysroot_src)
        };

        let sysroot = Sysroot {
            sysroot: base.join("fbcode/third-party-buck/platform010/build/rust"),
            sysroot_src: Some(sysroot_src),
        };

        return Ok(sysroot);
    }
    // Spawn both `rustc` and `buck audit config` in parallel without blocking.
    let fbsource_rustc = project_root.join("xplat/rust/toolchain/current/rustc");
    let mut sysroot_cmd = if cfg!(target_os = "macos") {
        // On Apple silicon, buck builds at Meta run under Rosetta.
        // So we force an x86-64 sysroot to avoid mixing architectures.
        let mut cmd = Command::new("arch");
        cmd.arg("-x86_64").arg(fbsource_rustc);
        cmd
    } else {
        Command::new(fbsource_rustc)
    };
    sysroot_cmd
        .arg("--print=sysroot")
        .stdin(Stdio::null())
        .stdout(Stdio::piped())
        .stderr(Stdio::piped());
    let sysroot_child = sysroot_cmd.spawn()?;

    let sysroot_src = buck.resolve_sysroot_src()?;
    let sysroot_src = if relative_paths {
        sysroot_src
    } else {
        project_root.join(sysroot_src)
    };

    // Now block while we wait for both processes.
    let mut sysroot = utf8_output(sysroot_child.wait_with_output(), &sysroot_cmd)
        .context("error asking rustc for sysroot")?;
    truncate_line_ending(&mut sysroot);

    let mut sysroot: PathBuf = sysroot.into();
    if relative_paths {
        sysroot = relative_to(&sysroot, project_root);
    }

    let sysroot = Sysroot {
        sysroot,
        sysroot_src: Some(sysroot_src),
    };

    Ok(sysroot)
}

#[instrument(ret)]
pub fn resolve_rustup_sysroot() -> Result<Sysroot, anyhow::Error> {
    let mut cmd = Command::new("rustc");
    cmd.arg("--print=sysroot")
        .stdin(Stdio::null())
        .stdout(Stdio::piped())
        .stderr(Stdio::piped());

    let output = utf8_output(cmd.output(), &cmd)?;
    let sysroot = PathBuf::from(output);

    let sysroot = Sysroot {
        sysroot,
        sysroot_src: None,
    };
    Ok(sysroot)
}


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

use std::collections::BTreeMap;
use std::ffi::OsStr;
use std::fmt;
use std::ops::Deref;
use std::path::Path;
use std::path::PathBuf;

use serde::Deserialize;
use serde::Deserializer;

use crate::json_project::Edition;

#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
pub struct Target(String);

impl Target {
    pub fn new<T>(target: T) -> Target
    where
        T: Into<String>,
    {
        let target: String = target.into();
        let target = target.split(' ').next().expect("Unable to get raw label");
        Target(target.to_owned())
    }
}

impl<'de> Deserialize<'de> for Target {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        let string: String = Deserialize::deserialize(deserializer)?;
        Ok(Target::new(string))
    }
}

impl Deref for Target {
    type Target = str;

    fn deref(&self) -> &Self::Target {
        self.0.as_str()
    }
}

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

/// Allows Target to be passed as an argument to Command.
impl AsRef<OsStr> for Target {
    fn as_ref(&self) -> &OsStr {
        OsStr::new(&self.0)
    }
}

impl AsRef<Path> for Target {
    fn as_ref(&self) -> &Path {
        Path::new(&self.0)
    }
}

impl AsRef<[u8]> for Target {
    fn as_ref(&self) -> &[u8] {
        self.0.as_bytes()
    }
}

#[derive(Debug, Clone, Deserialize, PartialEq, Eq)]
pub struct MacroOutput {
    pub actual: Target,
    pub dylib: PathBuf,
}
#[derive(Debug, Clone, Deserialize, PartialEq, Eq)]
pub enum Kind {
    #[serde(rename = "prelude//rules.bzl:rust_binary")]
    Binary,
    #[serde(rename = "prelude//rules.bzl:rust_library")]
    Library,
    #[serde(rename = "prelude//rules.bzl:rust_test")]
    Test,
}

#[derive(Debug, Clone, Deserialize, PartialEq, Eq)]
pub struct TargetInfo {
    pub name: String,
    pub label: String,
    pub kind: Kind,
    pub edition: Option<Edition>,
    pub srcs: Vec<PathBuf>,
    /// Mapped srcs are effectively aliases. The key is a buck target
    /// of some kind, and the value is a path/filename that can be
    /// referred to in the rest of the rule.
    ///
    /// Asking buck to build the targets and tell us the output path
    /// is how we are able to support generated sources.
    pub mapped_srcs: BTreeMap<PathBuf, PathBuf>,
    #[serde(rename = "crate")]
    pub crate_name: Option<String>,
    pub crate_root: Option<PathBuf>,
    #[serde(rename = "deps", alias = "buck.direct_dependencies", default)]
    pub deps: Vec<Target>,
    pub tests: Vec<Target>,
    // Optional set of renamed crates. in buck2, these are not unified with
    // `buck.direct_dependencies` and are instead a separate entry.
    pub named_deps: BTreeMap<String, Target>,
    pub proc_macro: Option<bool>,
    // Set of features enabled for this crate.
    pub features: Vec<String>,
    // The ensured folder containing symlinks to all sources
    pub source_folder: PathBuf,
}

#[derive(Debug, Clone, Deserialize, PartialEq, Eq)]
pub struct AliasedTargetInfo {
    pub actual: Target,
}

#[derive(Debug)]
pub struct TargetInfoEntry {
    pub index: usize,
    pub info: TargetInfo,
}

impl TargetInfo {
    pub fn crate_name(&self) -> String {
        self.crate_name.as_deref().map_or_else(
            || self.name.as_str().replace('-', "_"),
            |crate_name| crate_name.to_owned(),
        )
    }

    pub fn root_module(&self) -> PathBuf {
        // Matches buck crate_root fetching logic
        let root_candidates = if let Some(crate_root) = &self.crate_root {
            // If provided with one, use it
            vec![crate_root.to_owned()]
        } else {
            // Use buck rust build.bxl fallback logic
            vec![
                PathBuf::from("lib.rs"),
                PathBuf::from("main.rs"),
                PathBuf::from(&self.name.replace('-', "_")),
            ]
        };

        tracing::trace!(
            ?self,
            ?root_candidates,
            "trying to discover a good root module"
        );
        // for all normal sources, we need to reference the file on the fbcode tree so navigation works
        match self.srcs.iter().find(|src| {
            root_candidates
                .iter()
                .any(|candidate| src.ends_with(candidate))
        }) {
            // If a real source is provided, returns it's absolute path.
            // This will not work with crate using more than one target as a direct src.
            // Fortunately this is not used at the moment. Likely to be fixed in BXL instead
            Some(path) => return path.to_path_buf(),
            None => tracing::debug!(?self, "unable to find root for crate"),
        };

        for (dest, _) in self.mapped_srcs.iter() {
            if root_candidates.iter().any(|c| dest.ends_with(c)) {
                // Returns the files as seen in the materialized source
                return self.source_folder.join(dest);
            }
        }

        if let Some(fallback_path) = self.srcs.first().cloned().or_else(|| {
            self.mapped_srcs
                .keys()
                .next()
                .map(|mapped_path| self.source_folder.join(mapped_path))
        }) {
            return fallback_path;
        }

        tracing::error!(?self, "no crate root can be found");

        panic!("Invariant broken: rust-project is unable to determine a root module")
    }
}


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

use std::collections::BTreeMap;
use std::io::BufWriter;
use std::io::Write;
use std::path::Path;
use std::path::PathBuf;

use anyhow::bail;
use tracing::info;

use crate::buck;
use crate::buck::relative_to;
use crate::buck::to_json_project;
use crate::json_project::Sysroot;
use crate::sysroot::resolve_buckconfig_sysroot;
use crate::sysroot::resolve_rustup_sysroot;
use crate::sysroot::SysrootConfig;
use crate::target::Target;
use crate::target::TargetInfo;

pub struct Develop {
    pub input: Input,
    pub out: Output,
    pub sysroot: SysrootConfig,
    pub pretty: bool,
    pub relative_paths: bool,
}

impl From<crate::Command> for Develop {
    fn from(command: crate::Command) -> Self {
        if let crate::Command::Develop {
            targets,
            files,
            out,
            stdout,
            prefer_rustup_managed_toolchain,
            sysroot,
            pretty,
            relative_paths,
        } = command
        {
            let input = if !targets.is_empty() {
                Input::Targets(targets)
            } else {
                Input::Files(files)
            };

            let out = if stdout {
                Output::Stdout
            } else {
                Output::Path(out)
            };

            let sysroot = if prefer_rustup_managed_toolchain {
                SysrootConfig::Rustup
            } else if let Some(sysroot) = sysroot {
                SysrootConfig::Sysroot(sysroot)
            } else {
                SysrootConfig::BuckConfig
            };

            return Develop {
                input,
                out,
                sysroot,
                pretty,
                relative_paths,
            };
        }

        unreachable!("No other subcommand is supported.")
    }
}

pub enum Input {
    Targets(Vec<String>),
    Files(Vec<PathBuf>),
}

#[derive(Debug)]
pub enum Output {
    Path(PathBuf),
    Stdout,
}

impl Develop {
    pub fn run(self) -> Result<(), anyhow::Error> {
        let Develop {
            input,
            out,
            sysroot,
            pretty,
            relative_paths,
        } = self;
        let buck = buck::Buck;
        let project_root = buck.resolve_project_root()?;

        let targets = match input {
            Input::Targets(targets) => targets.iter().map(Target::new).collect::<Vec<Target>>(),
            // the owners query returns a `HashMap<PathBuf, Vec<Target>>`
            Input::Files(files) => buck.query_owner(files)?.into_values().flatten().collect(),
        };

        if targets.is_empty() {
            bail!("No targets can be inferred for the provided file.");
        }

        let targets = buck.expand_targets(&targets)?;

        let target_map: BTreeMap<Target, TargetInfo> = buck.resolve_deps(&targets)?;
        let aliased_libraries = buck.query_aliased_libraries(&targets)?;
        let proc_macros = buck.query_proc_macros(&targets)?;

        let sysroot = match &sysroot {
            SysrootConfig::Sysroot(path) => {
                let mut sysroot_path = expand_tilde(path)?.canonicalize()?;
                if relative_paths {
                    sysroot_path = relative_to(&sysroot_path, &project_root);
                }

                Sysroot {
                    sysroot: sysroot_path,
                    sysroot_src: None,
                }
            }
            SysrootConfig::BuckConfig => {
                let project_root = buck.resolve_project_root()?;
                resolve_buckconfig_sysroot(&project_root, relative_paths)?
            }
            SysrootConfig::Rustup => resolve_rustup_sysroot()?,
        };
        info!("converting buck info to rust-project.json");
        let rust_project = to_json_project(
            sysroot,
            targets,
            target_map,
            aliased_libraries,
            proc_macros,
            relative_paths,
        )?;

        let mut writer: BufWriter<Box<dyn Write>> = match out {
            Output::Path(ref p) => {
                let out = std::fs::File::create(p)?;
                BufWriter::new(Box::new(out))
            }
            Output::Stdout => BufWriter::new(Box::new(std::io::stdout())),
        };

        if pretty {
            serde_json::to_writer_pretty(&mut writer, &rust_project)?;
        } else {
            serde_json::to_writer(&mut writer, &rust_project)?;
        }

        match out {
            Output::Path(p) => info!(file = ?p, "wrote rust-project.json"),
            Output::Stdout => info!("wrote rust-project.json to stdout"),
        }

        Ok(())
    }
}

fn expand_tilde(path: &Path) -> Result<PathBuf, anyhow::Error> {
    if path.starts_with("~") {
        let path = path.strip_prefix("~")?;
        let home = std::env::var("HOME")?;
        let home = PathBuf::from(home);
        Ok(home.join(path))
    } else {
        Ok(path.to_path_buf())
    }
}


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

mod develop;
mod new;

pub use develop::Develop;
pub use new::New;
pub use new::ProjectKind;