aopt-help 0.3.6

Generate help message for command line program
Documentation
use std::borrow::Cow;
use std::ops::Deref;
use std::ops::DerefMut;

use crate::block::Block;
use crate::error::Error;
use crate::format::HelpDisplay;
use crate::format::HelpPolicy;
use crate::store::Store;

#[derive(Debug, Default, Clone)]
pub struct Command<'a> {
    name: Cow<'a, str>,

    hint: Cow<'a, str>,

    help: Cow<'a, str>,

    foot: Cow<'a, str>,

    head: Cow<'a, str>,

    blocks: Vec<Block<'a, Cow<'a, str>>>,

    stores: Vec<Store<'a>>,
}

impl<'a> Command<'a> {
    pub fn new<S: Into<Cow<'a, str>>>(name: S, hint: S, help: S, head: S, foot: S) -> Self {
        Self {
            name: name.into(),
            foot: foot.into(),
            head: head.into(),
            hint: hint.into(),
            help: help.into(),
            blocks: vec![],
            stores: vec![],
        }
    }

    pub fn name(&self) -> Cow<'a, str> {
        self.name.clone()
    }

    pub fn hint(&self) -> Cow<'a, str> {
        self.hint.clone()
    }

    pub fn help(&self) -> Cow<'a, str> {
        self.help.clone()
    }

    pub fn head(&self) -> Cow<'a, str> {
        self.head.clone()
    }

    pub fn foot(&self) -> Cow<'a, str> {
        self.foot.clone()
    }

    pub fn block(&self) -> &Vec<Block<'a, Cow<'a, str>>> {
        &self.blocks
    }

    pub fn block_mut(&mut self) -> &mut Vec<Block<'a, Cow<'a, str>>> {
        &mut self.blocks
    }

    pub fn has_position(&self) -> bool {
        self.stores.iter().any(|store| store.position())
    }

    pub fn find_store<S: Into<Cow<'a, str>>>(&self, name: S) -> Option<&Store<'a>> {
        let name = name.into();
        self.stores.iter().find(|v| v.name() == name)
    }

    pub fn find_store_mut<S: Into<Cow<'a, str>>>(&mut self, name: S) -> Option<&mut Store<'a>> {
        let name = name.into();
        self.stores.iter_mut().find(|v| v.name() == name)
    }

    pub fn find_block<S: Into<Cow<'a, str>>>(&self, name: S) -> Option<&Block<'a, Cow<'a, str>>> {
        let name = name.into();
        self.blocks.iter().find(|v| v.name() == name)
    }

    pub fn find_block_mut<S: Into<Cow<'a, str>>>(
        &mut self,
        name: S,
    ) -> Option<&mut Block<'a, Cow<'a, str>>> {
        let name = name.into();
        self.blocks.iter_mut().find(|v| v.name() == name)
    }

    pub fn set_name<S: Into<Cow<'a, str>>>(&mut self, name: S) -> &mut Self {
        self.name = name.into();
        self
    }

    pub fn set_hint<S: Into<Cow<'a, str>>>(&mut self, hint: S) -> &mut Self {
        self.hint = hint.into();
        self
    }

    pub fn set_help<S: Into<Cow<'a, str>>>(&mut self, help: S) -> &mut Self {
        self.help = help.into();
        self
    }

    pub fn set_foot<S: Into<Cow<'a, str>>>(&mut self, help: S) -> &mut Self {
        self.foot = help.into();
        self
    }

    pub fn set_head<S: Into<Cow<'a, str>>>(&mut self, help: S) -> &mut Self {
        self.head = help.into();
        self
    }

    pub fn add_store<S: Into<Cow<'a, str>>>(
        &mut self,
        block: S,
        store: Store<'a>,
    ) -> Result<&mut Self, Error> {
        let name: Cow<'a, str> = block.into();

        if self.find_store(name.clone()).is_some() {
            Err(Error::DuplicatedStoreName(store.name().to_string()))
        } else {
            let block = self
                .find_block_mut(name.clone())
                .ok_or_else(|| Error::InvalidBlockName(name.to_string()))?;

            block.attach(store.name());
            self.stores.push(store);
            Ok(self)
        }
    }

    pub fn add_block(&mut self, block: Block<'a, Cow<'a, str>>) -> Result<&mut Self, Error> {
        let name = block.name();

        if self.find_block(name.clone()).is_some() {
            Err(Error::DuplicatedBlockName(name.to_string()))
        } else {
            self.blocks.push(block);
            Ok(self)
        }
    }

    pub fn new_store<S: Into<Cow<'a, str>>>(
        &mut self,
        block: S,
        name: S,
    ) -> Result<AddStore2Block<'a, '_>, Error> {
        let name = name.into();

        if self.find_store(name.clone()).is_some() {
            Err(Error::DuplicatedStoreName(name.to_string()))
        } else {
            let block = block.into();
            let block = self
                .blocks
                .iter_mut()
                .find(|v| v.name() == block)
                .ok_or_else(|| Error::InvalidBlockName(block.to_string()))?;
            let stores = &mut self.stores;

            Ok(AddStore2Block::new(block, stores, name))
        }
    }

    pub fn new_block<S: Into<Cow<'a, str>>>(&mut self, name: S) -> Result<BlockMut<'a, '_>, Error> {
        let name = name.into();

        if self.find_block(name.clone()).is_some() {
            Err(Error::DuplicatedBlockName(name.to_string()))
        } else {
            Ok(BlockMut::new(self, name))
        }
    }
}

impl<'a> Deref for Command<'a> {
    type Target = Vec<Store<'a>>;

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

impl DerefMut for Command<'_> {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.stores
    }
}

impl HelpDisplay for Command<'_> {
    fn gen_help<'a, P>(&self, policy: &P) -> Option<Cow<'a, str>>
    where
        Self: 'a,
        P: HelpPolicy<'a, Self>,
    {
        policy.format(self)
    }
}

pub struct AddStore2Block<'a, 'b> {
    store: Store<'a>,

    block: &'b mut Block<'a, Cow<'a, str>>,

    stores: &'b mut Vec<Store<'a>>,

    added: bool,
}

impl<'a, 'b> AddStore2Block<'a, 'b> {
    pub fn new<S: Into<Cow<'a, str>>>(
        block: &'b mut Block<'a, Cow<'a, str>>,
        stores: &'b mut Vec<Store<'a>>,
        name: S,
    ) -> Self {
        let mut store = Store::default();

        store.set_optional(true);
        store.set_name(name);
        Self {
            block,
            store,
            stores,
            added: false,
        }
    }

    pub fn set_optional(&mut self, optional: bool) -> &mut Self {
        self.store.set_optional(optional);
        self
    }

    pub fn set_position(&mut self, position: bool) -> &mut Self {
        self.store.set_position(position);
        self
    }

    pub fn set_hint<S: Into<Cow<'a, str>>>(&mut self, hint: S) -> &mut Self {
        self.store.set_hint(hint);
        self
    }

    pub fn set_help<S: Into<Cow<'a, str>>>(&mut self, help: S) -> &mut Self {
        self.store.set_help(help);
        self
    }

    pub fn set_name<S: Into<Cow<'a, str>>>(&mut self, name: S) -> &mut Self {
        self.store.set_name(name);
        self
    }

    pub fn set_type<S: Into<Cow<'a, str>>>(&mut self, type_name: S) -> &mut Self {
        self.store.set_type(type_name);
        self
    }

    pub fn submit(mut self) {
        if !self.added {
            let store = std::mem::take(&mut self.store);

            self.block.attach(store.name());
            self.stores.push(store);
            self.added = true;
        }
    }
}

impl Drop for AddStore2Block<'_, '_> {
    fn drop(&mut self) {
        if !self.added {
            let store = std::mem::take(&mut self.store);

            self.block.attach(store.name());
            self.stores.push(store);
            self.added = true;
        }
    }
}

pub struct BlockMut<'a, 'b> {
    block: Block<'a, Cow<'a, str>>,

    cmd: &'b mut Command<'a>,

    added: bool,
}

impl<'a, 'b> BlockMut<'a, 'b> {
    pub fn new<S: Into<Cow<'a, str>>>(cmd: &'b mut Command<'a>, name: S) -> Self {
        let mut opt = Block::default();

        opt.set_name(name);
        Self {
            cmd,
            block: opt,
            added: false,
        }
    }

    pub fn attach<S: Into<Cow<'a, str>>>(&mut self, name: S) -> Result<&mut Self, Error> {
        let name = name.into();

        if self.cmd.find_store(name.clone()).is_none() {
            Err(Error::InvalidStoreName(name.to_string()))
        } else if self.block.iter().any(|v| v == &name) {
            Err(Error::DuplicatedStoreName(name.to_string()))
        } else {
            self.block.attach(name);
            Ok(self)
        }
    }

    pub fn set_hint<S: Into<Cow<'a, str>>>(&mut self, hint: S) -> &mut Self {
        self.block.set_hint(hint);
        self
    }

    pub fn set_help<S: Into<Cow<'a, str>>>(&mut self, help: S) -> &mut Self {
        self.block.set_help(help);
        self
    }

    pub fn set_foot<S: Into<Cow<'a, str>>>(&mut self, footer: S) -> &mut Self {
        self.block.set_foot(footer);
        self
    }

    pub fn set_head<S: Into<Cow<'a, str>>>(&mut self, header: S) -> &mut Self {
        self.block.set_head(header);
        self
    }

    pub fn set_name<S: Into<Cow<'a, str>>>(&mut self, name: S) -> &mut Self {
        self.block.set_name(name);
        self
    }

    pub fn new_store<S: Into<Cow<'a, str>>>(&mut self, store: S) -> AddStore2Block<'a, '_> {
        let block = &mut self.block;
        let stores = &mut self.cmd.stores;

        AddStore2Block::new(block, stores, store)
    }

    pub fn submit(mut self) {
        if !self.added {
            let block = std::mem::take(&mut self.block);

            self.cmd.add_block(block).unwrap();
            self.added = true;
        }
    }
}

impl Drop for BlockMut<'_, '_> {
    fn drop(&mut self) {
        if !self.added {
            let block = std::mem::take(&mut self.block);
            let cmd_name = self.cmd.name();

            self.cmd
                .add_block(block)
                .unwrap_or_else(|_| panic!("Can not add block to Command {cmd_name}"));
        }
    }
}