use super::asset::Assets;
use super::standard_properties::LocalStandardProperties;
use crate::common::{container_file_of, container_settings_file_of};
use crate::project::asset;
use crate::project::container::path_is_container;
use crate::result::{Error, Result};
use cluFlock::FlockLock;
use serde::{Deserialize, Serialize};
use settings_manager::local_settings::{LocalSettings, LockSettingsFile};
use settings_manager::result::{
Error as SettingsError, LocalSettingsError, Result as SettingsResult,
};
use settings_manager::settings::Settings;
use settings_manager::types::Priority as SettingsPriority;
use std::collections::HashMap;
use std::fs::{self, File};
use std::path::{Path, PathBuf};
use thot_core::project::container::{AssetMap, ContainerMap, ScriptMap};
use thot_core::project::{Container as CoreContainer, ScriptAssociation, StandardProperties};
use thot_core::result::{ContainerError, Error as CoreError, ProjectError, ResourceError};
use thot_core::types::resource_map::serialize_resource_map_keys_only;
use thot_core::types::{ResourceId, UserPermissions};
#[derive(Serialize, Deserialize, Debug)]
pub struct Container {
#[serde(skip)]
_file_lock: Option<FlockLock<File>>,
#[serde(skip)]
_base_path: Option<PathBuf>,
pub properties: StandardProperties,
#[serde(with = "serialize_resource_map_keys_only")]
pub children: ContainerMap,
#[serde(with = "serialize_resource_map_keys_only")]
pub assets: AssetMap,
#[serde(with = "serialize_scripts")]
pub scripts: ScriptMap,
}
impl Container {
pub fn new() -> Result<Self> {
let props = LocalStandardProperties::new()?;
Ok(Container {
_file_lock: None,
_base_path: None,
properties: props,
children: ContainerMap::new(),
assets: AssetMap::new(),
scripts: ScriptMap::new(),
})
}
pub fn add_asset(&mut self, path: &Path) -> Result<ResourceId> {
let cont_path = match self._base_path.clone() {
Some(p) => p,
None => {
return Err(Error::SettingsError(SettingsError::LocalSettingsError(
LocalSettingsError::PathNotSet,
)))
}
};
let rid = asset::init(path, Some(cont_path.as_path()))?;
self.register_asset(rid.clone())?;
Ok(rid)
}
pub fn register_asset(&mut self, rid: ResourceId) -> Result<bool> {
let bp = match self._base_path.clone() {
Some(p) => p,
None => {
return Err(Error::SettingsError(SettingsError::LocalSettingsError(
LocalSettingsError::PathNotSet,
)))
}
};
let assets = Assets::load(bp.as_path())?;
if !assets.contains(&rid) {
return Err(Error::CoreError(CoreError::ResourceError(
ResourceError::DoesNotExist(format!(
"Asset with {:?} has not been initialized",
rid
)),
)));
}
if self.assets.contains_key(&rid) {
return Ok(false);
}
self.assets.insert(rid, None);
Ok(true)
}
pub fn deregister_asset(&mut self, rid: &ResourceId) -> bool {
if !self.assets.contains_key(&rid) {
return false;
}
self.assets.remove(rid);
true
}
pub fn register_child(&mut self, rid: ResourceId) -> bool {
if self.children.contains_key(&rid) {
return false;
}
self.children.insert(rid, None);
true
}
pub fn deregister_child(&mut self, rid: &ResourceId) -> bool {
if !self.children.contains_key(rid) {
return false;
}
self.children.remove(rid);
true
}
pub fn child_path(&self, rid: &ResourceId) -> Result<PathBuf> {
if !self.children.contains_key(rid) {
return Err(Error::CoreError(CoreError::ProjectError(
ProjectError::NotRegistered(Some(rid.clone()), None),
)));
}
let r_path = self.base_path()?;
for entry in fs::read_dir(&r_path)? {
let entry = entry?;
let e_type = entry.file_type()?;
if e_type.is_dir() {
let p = entry.path();
if !path_is_container(&p) {
continue;
}
let child = Self::load(&p)?;
if &child.properties.rid == rid {
return Ok(p);
}
}
}
Err(Error::CoreError(CoreError::ContainerError(
ContainerError::MissingChild(rid.clone()),
)))
}
pub fn children_paths(&self) -> Result<Vec<PathBuf>> {
let r_path = self.base_path()?;
let mut c_paths = Vec::new();
for entry in fs::read_dir(&r_path)? {
let entry = entry?;
let e_type = entry.file_type()?;
if e_type.is_dir() {
let p = entry.path();
if !path_is_container(&p) {
continue;
}
let child = Self::load(&p)?;
if self.children.contains_key(&child.properties.rid) {
c_paths.push(p);
}
}
}
Ok(c_paths)
}
pub fn contains_script_association(&self, rid: &ResourceId) -> bool {
self.scripts.get(rid).is_some()
}
pub fn add_script_association(&mut self, assoc: ScriptAssociation) -> Result {
if self.contains_script_association(&assoc.script) {
return Err(Error::CoreError(CoreError::ResourceError(
ResourceError::AlreadyExists(String::from(
"Association with script already exists",
)),
)));
}
let script = assoc.script.clone();
self.scripts.insert(script, assoc.into());
Ok(())
}
pub fn set_script_association(&mut self, assoc: ScriptAssociation) -> Result<bool> {
let script = assoc.script.clone();
let old = self.scripts.insert(script, assoc.into());
Ok(old.is_none())
}
pub fn remove_script_association(&mut self, rid: &ResourceId) -> bool {
let old = self.scripts.remove(rid);
old.is_some()
}
}
impl Default for Container {
fn default() -> Self {
Container {
_file_lock: None,
_base_path: None,
properties: StandardProperties::default(),
children: HashMap::new(),
assets: HashMap::new(),
scripts: HashMap::new(),
}
}
}
impl From<CoreContainer> for Container {
fn from(container: CoreContainer) -> Self {
Container {
_file_lock: None,
_base_path: None,
properties: container.properties,
children: container.children,
assets: container.assets,
scripts: container.scripts,
}
}
}
impl Settings for Container {
fn store_lock(&mut self, lock: FlockLock<File>) {
self._file_lock = Some(lock);
}
fn controls_file(&self) -> bool {
self._file_lock.is_some()
}
fn priority(&self) -> SettingsPriority {
SettingsPriority::Project
}
}
impl LocalSettings for Container {
fn rel_path() -> SettingsResult<PathBuf> {
Ok(container_file_of(Path::new("")))
}
fn base_path(&self) -> SettingsResult<PathBuf> {
self._base_path
.clone()
.ok_or(SettingsError::LocalSettingsError(
LocalSettingsError::PathNotSet,
))
}
fn set_base_path(&mut self, path: PathBuf) -> SettingsResult {
self._base_path = Some(path);
Ok(())
}
}
impl LockSettingsFile for Container {}
mod serialize_scripts {
use super::ScriptMap;
use serde::de;
use serde::ser::{SerializeSeq, Serializer};
use std::fmt;
use std::result::Result as StdResult;
use thot_core::project::ScriptAssociation;
struct AssocVisitor;
impl<'de> de::Visitor<'de> for AssocVisitor {
type Value = ScriptMap;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("a ScriptMap (HashMap<ResourceId, RunParameters>)")
}
fn visit_seq<A>(self, mut seq: A) -> StdResult<Self::Value, A::Error>
where
A: de::SeqAccess<'de>,
{
let mut scripts: ScriptMap;
match seq.size_hint() {
None => scripts = ScriptMap::new(),
Some(s) => scripts = ScriptMap::with_capacity(s),
};
while let Some(assoc) = seq.next_element::<ScriptAssociation>()? {
scripts.insert(assoc.script.clone(), assoc.into());
}
Ok(scripts)
}
}
pub fn serialize<S>(scripts: &ScriptMap, serializer: S) -> StdResult<S::Ok, S::Error>
where
S: Serializer,
{
let mut seq = serializer.serialize_seq(Some(scripts.len()))?;
for (rid, params) in scripts.iter() {
let assoc = params.clone().to_association(rid.clone());
seq.serialize_element(&assoc)?;
}
seq.end()
}
pub fn deserialize<'de, D>(deserializer: D) -> StdResult<ScriptMap, D::Error>
where
D: de::Deserializer<'de>,
{
deserializer.deserialize_seq(AssocVisitor)
}
}
#[derive(Serialize, Deserialize, Debug, Default)]
pub struct ContainerSettings {
#[serde(skip)]
_file_lock: Option<FlockLock<File>>,
#[serde(skip)]
_base_path: Option<PathBuf>,
pub permissions: Vec<UserPermissions>,
}
impl ContainerSettings {
pub fn new() -> Self {
ContainerSettings {
_file_lock: None,
_base_path: None,
permissions: Vec::new(),
}
}
}
impl Settings for ContainerSettings {
fn store_lock(&mut self, lock: FlockLock<File>) {
self._file_lock = Some(lock);
}
fn controls_file(&self) -> bool {
self._file_lock.is_some()
}
fn priority(&self) -> SettingsPriority {
SettingsPriority::Project
}
}
impl LocalSettings for ContainerSettings {
fn rel_path() -> SettingsResult<PathBuf> {
Ok(container_settings_file_of(Path::new("")))
}
fn base_path(&self) -> SettingsResult<PathBuf> {
self._base_path
.clone()
.ok_or(SettingsError::LocalSettingsError(
LocalSettingsError::PathNotSet,
))
}
fn set_base_path(&mut self, path: PathBuf) -> SettingsResult {
self._base_path = Some(path);
Ok(())
}
}
impl LockSettingsFile for ContainerSettings {}
#[cfg(test)]
#[path = "./container_test.rs"]
mod container_test;