library;

use ::bytes::Bytes;
use ::option::Option::{self, *};
use ::storage::{storable_slice::*, storage_key::StorageKey};
use ::storage::storage_api::read_quads;
use ::string::String;
use ::codec::*;
use ::debug::*;

/// A storage type for storing a UTF-8 encoded string,
/// as a collection of tightly packed bytes.
pub struct StorageString {}

// Note: `StorageString` is a zero-sized storage type that can be nested
//       within other storage types. For example, a `StorageMap<K, StorageString>`.
//       That's why we **always use the `self.field_id`** as a storage slot
//       for all of the methods of `StorageString`, and **never the `self.slot`**.

#[cfg(experimental_dynamic_storage = false)]
impl StorableSlice<String> for StorageKey<StorageString> {
    /// Takes a `String` type and saves the underlying data in storage.
    ///
    /// # Arguments
    ///
    /// * `string`: [String] - The string which will be stored.
    ///
    /// # Number of Storage Accesses
    ///
    /// * Reads: `1` (to read the existing data in the slice length slot)
    /// * Writes: `2` (one for the `String` length, and one for the content)
    ///
    /// # Examples
    ///
    /// ```sway
    /// use std::{storage::storage_string::StorageString, string::String};
    ///
    /// storage {
    ///     stored_string: StorageString = StorageString {}
    /// }
    ///
    /// fn foo() {
    ///     let string = String::from_ascii_str("Fuel is blazingly fast");
    ///
    ///     storage.stored_string.write_slice(string);
    /// }
    /// ```
    #[storage(read, write)]
    fn write_slice(self, string: String) {
        write_slice_quads(self.field_id(), string.as_raw_slice());
    }

    /// Constructs a `String` type from a collection of tightly packed bytes in storage.
    ///
    /// # Returns
    ///
    /// * [Option<String>] - The valid `String` stored, otherwise `None`.
    ///
    /// # Number of Storage Accesses
    ///
    /// * Reads: `2` (one for the `String` length, and one for the content)
    ///
    /// # Examples
    ///
    /// ```sway
    /// use std::{storage::storage_string::StorageString, string::String};
    ///
    /// storage {
    ///     stored_string: StorageString = StorageString {}
    /// }
    ///
    /// fn foo() {
    ///     let string = String::from_ascii_str("Fuel is blazingly fast");
    ///
    ///     assert(storage.stored_string.read_slice().is_none());
    ///     storage.stored_string.write_slice(string);
    ///     let retrieved_string = storage.stored_string.read_slice().unwrap();
    ///     assert_eq(string, retrieved_string);
    /// }
    /// ```
    #[storage(read)]
    fn read_slice(self) -> Option<String> {
        match read_slice_quads(self.field_id()) {
            Some(slice) => {
                // `read_slice_quads` returns a freshly heap-allocated slice that
                // is used only here, so we take its ownership instead of copying.
                Some(String::from_moved_raw_slice(slice))
            },
            None => None,
        }
    }

    /// Clears a stored `String` in storage.
    ///
    /// # Returns
    ///
    /// * [bool] - `true` if _all_ of the cleared storage slots were previously set. Otherwise, `false`.
    ///
    /// # Number of Storage Accesses
    ///
    /// * Reads: `1` (to determine the `String` length)
    /// * Clears: `2` (one for the `String` length, and one for the content)
    ///
    /// # Examples
    ///
    /// ```sway
    /// use std::{storage::storage_string::StorageString, string::String};
    ///
    /// storage {
    ///     stored_string: StorageString = StorageString {}
    /// }
    ///
    /// fn foo() {
    ///     let string = String::from_ascii_str("Fuel is blazingly fast");
    ///
    ///     storage.stored_string.write_slice(string);
    ///
    ///     assert(storage.stored_string.read_slice().is_some());
    ///     let cleared = storage.stored_string.clear();
    ///     assert(cleared);
    ///     let retrieved_string = storage.stored_string.read_slice();
    ///     assert(retrieved_string.is_none());
    /// }
    /// ```
    #[storage(read, write)]
    fn clear(self) -> bool {
        clear_slice_quads(self.field_id())
    }

    /// Returns the length of a `String` in storage, in bytes.
    ///
    /// # Returns
    ///
    /// * [u64] - The length of the `String` in storage, in bytes, or `0` if there is no valid `String` in storage.
    ///
    /// # Number of Storage Accesses
    ///
    /// * Reads: `1` (to read the `String` length)
    ///
    /// # Examples
    ///
    /// ```sway
    /// use std::{storage::storage_string::StorageString, string::String};
    ///
    /// storage {
    ///     stored_string: StorageString = StorageString {}
    /// }
    ///
    /// fn foo() {
    ///     let string = String::from_ascii_str("Fuel is blazingly fast");
    ///
    ///     assert_eq!(storage.stored_string.len(), 0);
    ///     storage.stored_string.write_slice(string);
    ///     assert_eq!(storage.stored_string.len(), 22);
    /// }
    /// ```
    #[storage(read)]
    fn len(self) -> u64 {
        read_quads::<u64>(self.field_id(), 0).unwrap_or(0)
    }
}

#[cfg(experimental_dynamic_storage = true)]
impl StorableSlice<String> for StorageKey<StorageString> {
    /// Takes a `String` type and saves the underlying data in storage.
    ///
    /// # Arguments
    ///
    /// * `string`: [String] - The string which will be stored.
    ///
    /// # Number of Storage Accesses
    ///
    /// * Writes: `1` (for storing the `String` content)
    ///
    /// # Examples
    ///
    /// ```sway
    /// use std::{storage::storage_string::StorageString, string::String};
    ///
    /// storage {
    ///     stored_string: StorageString = StorageString {}
    /// }
    ///
    /// fn foo() {
    ///     let string = String::from_ascii_str("Fuel is blazingly fast");
    ///
    ///     storage.stored_string.write_slice(string);
    /// }
    /// ```
    #[storage(write)]
    fn write_slice(self, string: String) {
        write_slice_slot(self.field_id(), string.as_raw_slice());
    }

    /// Constructs a `String` type from a collection of tightly packed bytes in storage.
    ///
    /// # Returns
    ///
    /// * [Option<String>] - The valid `String` stored, otherwise `None`.
    ///
    /// # Number of Storage Accesses
    ///
    /// * Preloads: `1` (for the `String` length)
    /// * Reads: `1` (for the `String` content)
    ///
    /// # Examples
    ///
    /// ```sway
    /// use std::{storage::storage_string::StorageString, string::String};
    ///
    /// storage {
    ///     stored_string: StorageString = StorageString {}
    /// }
    ///
    /// fn foo() {
    ///     let string = String::from_ascii_str("Fuel is blazingly fast");
    ///
    ///     assert(storage.stored_string.read_slice().is_none());
    ///     storage.stored_string.write_slice(string);
    ///     let retrieved_string = storage.stored_string.read_slice().unwrap();
    ///     assert_eq(string, retrieved_string);
    /// }
    /// ```
    #[storage(read)]
    fn read_slice(self) -> Option<String> {
        match read_slice_slot(self.field_id()) {
            Some(slice) => {
                // `read_slice_slot` returns a freshly heap-allocated slice that
                // is used only here, so we take its ownership instead of copying.
                Some(String::from_moved_raw_slice(slice))
            },
            None => None,
        }
    }

    /// Clears a stored `String` in storage.
    ///
    /// # Number of Storage Accesses
    ///
    /// * Clears: `1` (for the `String` content)
    ///
    /// # Examples
    ///
    /// ```sway
    /// use std::{storage::storage_string::StorageString, string::String};
    ///
    /// storage {
    ///     stored_string: StorageString = StorageString {}
    /// }
    ///
    /// fn foo() {
    ///     let string = String::from_ascii_str("Fuel is blazingly fast");
    ///
    ///     storage.stored_string.write_slice(string);
    ///
    ///     assert(storage.stored_string.read_slice().is_some());
    ///     storage.stored_string.clear();
    ///     let retrieved_string = storage.stored_string.read_slice();
    ///     assert(retrieved_string.is_none());
    /// }
    /// ```
    #[storage(write)]
    fn clear(self) {
        clear_slice_slot(self.field_id())
    }

    /// Clears a stored `String` in storage and returns whether it existed.
    ///
    /// # Returns
    ///
    /// * [bool] - `true` if the cleared storage slot was previously set. Otherwise, `false`.
    ///
    /// # Number of Storage Accesses
    ///
    /// * Preload: `1` (to determine if the slot was previously set)
    /// * Clears: `1` (for the `String` content)
    ///
    /// # Examples
    ///
    /// ```sway
    /// use std::{storage::storage_string::StorageString, string::String};
    ///
    /// storage {
    ///     stored_string: StorageString = StorageString {}
    /// }
    ///
    /// fn foo() {
    ///     let string = String::from_ascii_str("Fuel is blazingly fast");
    ///
    ///     storage.stored_string.write_slice(string);
    ///
    ///     assert(storage.stored_string.read_slice().is_some());
    ///     let cleared = storage.stored_string.clear();
    ///     assert(cleared);
    ///     let retrieved_string = storage.stored_string.read_slice();
    ///     assert(retrieved_string.is_none());
    /// }
    /// ```
    #[storage(read, write)]
    fn clear_existed(self) -> bool {
        clear_slice_slot_existed(self.field_id())
    }

    /// Returns the length of a `String` in storage, in bytes.
    ///
    /// # Returns
    ///
    /// * [u64] - The length of the `String` in storage, in bytes, or `0` if there is no valid `String` in storage.
    ///
    /// # Number of Storage Accesses
    ///
    /// * Preloads: `1` (to read the `String` length)
    ///
    /// # Examples
    ///
    /// ```sway
    /// use std::{storage::storage_string::StorageString, string::String};
    ///
    /// storage {
    ///     stored_string: StorageString = StorageString {}
    /// }
    ///
    /// fn foo() {
    ///     let string = String::from_ascii_str("Fuel is blazingly fast");
    ///
    ///     assert_eq!(storage.stored_string.len(), 0);
    ///     storage.stored_string.write_slice(string);
    ///     assert_eq!(storage.stored_string.len(), 22);
    /// }
    /// ```
    #[storage(read)]
    fn len(self) -> u64 {
        __state_preload(self.field_id())
    }
}