// Copyright (c) The Libra Core Contributors
// SPDX-License-Identifier: Apache-2.0

//! An identifier is the name of an entity (module, resource, function, etc) in Move.
//!
//! Among other things, identifiers are used to:
//! * specify keys for lookups in storage
//! * do cross-module lookups while executing transactions

// TODO: restrict identifiers to a subset of ASCII

use failure::prelude::*;
#[cfg(any(test, feature = "testing"))]
use proptest::prelude::*;
use serde::{Deserialize, Serialize};
use solana_libra_canonical_serialization::{
    CanonicalDeserialize, CanonicalDeserializer, CanonicalSerialize, CanonicalSerializer,
};
use std::{borrow::Borrow, fmt, ops::Deref};

/// An owned identifier.
///
/// For more details, see the module level documentation.
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize, Deserialize)]
pub struct Identifier(Box<str>);
// An identifier cannot be mutated so use Box<str> instead of String -- it is 1 word smaller.

impl Identifier {
    /// Creates a new `Identifier` instance.
    pub fn new(s: impl Into<Box<str>>) -> Result<Self> {
        // TODO: restrict identifiers to a subset of ASCII
        Ok(Self(s.into()))
    }

    /// Converts a vector of bytes to an `Identifier`.
    pub fn from_utf8(vec: Vec<u8>) -> Result<Self> {
        let s = String::from_utf8(vec)?;
        Self::new(s)
    }

    /// Creates a borrowed version of `self`.
    pub fn as_ident_str(&self) -> &IdentStr {
        self
    }

    /// Converts this `Identifier` into a `String`.
    ///
    /// This is not implemented as a `From` trait to discourage automatic conversions -- these
    /// conversions should not typically happen.
    pub fn into_string(self) -> String {
        self.0.into()
    }

    /// Converts this `Identifier` into a UTF-8-encoded byte sequence.
    pub fn into_bytes(self) -> Vec<u8> {
        self.into_string().into_bytes()
    }
}

impl<'a> From<&'a IdentStr> for Identifier {
    fn from(ident_str: &'a IdentStr) -> Self {
        ident_str.to_owned()
    }
}

impl AsRef<IdentStr> for Identifier {
    fn as_ref(&self) -> &IdentStr {
        self
    }
}

impl Deref for Identifier {
    type Target = IdentStr;

    fn deref(&self) -> &IdentStr {
        // Identifier and IdentStr maintain the same invariants, so it is safe to use
        // str_to_ident_str.
        str_to_ident_str(&self.0)
    }
}

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

/// A borrowed identifier.
///
/// For more details, see the module level documentation.
#[derive(Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[repr(transparent)]
pub struct IdentStr(str);

impl IdentStr {
    pub fn new(s: &str) -> Result<&IdentStr> {
        // TODO: restrict identifiers to a subset of ASCII.
        Ok(str_to_ident_str(s))
    }

    /// Returns the length of `self` in bytes.
    pub fn len(&self) -> usize {
        self.0.len()
    }

    /// Returns `true` if `self` has a length of zero bytes.
    pub fn is_empty(&self) -> bool {
        self.0.is_empty()
    }

    /// Converts `self` to a `&str`.
    ///
    /// This is not implemented as a `From` trait to discourage automatic conversions -- these
    /// conversions should not typically happen.
    pub fn as_str(&self) -> &str {
        &self.0
    }

    /// Converts `self` to a byte slice.
    pub fn as_bytes(&self) -> &[u8] {
        self.0.as_bytes()
    }
}

impl Borrow<IdentStr> for Identifier {
    fn borrow(&self) -> &IdentStr {
        self
    }
}

impl ToOwned for IdentStr {
    type Owned = Identifier;

    fn to_owned(&self) -> Identifier {
        Identifier(self.0.into())
    }
}

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

// This function is private -- it is used by code within this module once it has verified
// identifier invariants.
fn str_to_ident_str(s: &str) -> &IdentStr {
    // IdentStr and str have the same layout, so this is safe to do.
    // This follows the pattern in Rust core https://doc.rust-lang.org/src/std/path.rs.html.
    unsafe { &*(s as *const str as *const IdentStr) }
}

#[cfg(any(test, feature = "testing"))]
impl Arbitrary for Identifier {
    type Parameters = ();
    type Strategy = BoxedStrategy<Self>;

    fn arbitrary_with((): ()) -> Self::Strategy {
        // TODO: restrict identifiers to a subset of ASCII
        ".*".prop_map(|s| Identifier::new(s).unwrap()).boxed()
    }
}

/// LCS does not define any sort of extra annotation for identifiers -- they're serialized exactly
/// the same way regular strings are, and are represented only within the type system for now.
impl CanonicalSerialize for Identifier {
    fn serialize(&self, serializer: &mut impl CanonicalSerializer) -> Result<()> {
        serializer.encode_string(&self.0)?;
        Ok(())
    }
}

/// LCS does not define any sort of extra annotation for user strings -- they're serialized exactly
/// the same way regular strings are, and are represented only within the type system for now.
impl CanonicalSerialize for IdentStr {
    fn serialize(&self, serializer: &mut impl CanonicalSerializer) -> Result<()> {
        serializer.encode_string(&self.0)?;
        Ok(())
    }
}

impl CanonicalDeserialize for Identifier {
    fn deserialize(deserializer: &mut impl CanonicalDeserializer) -> Result<Self> {
        Identifier::new(deserializer.decode_string()?)
    }
}
