blockset-lib 0.7.0

BLOCKSET internal library
Documentation
use std::io::{self, Cursor, Write};

use io_trait::Io;
use nanvm_lib::{
    common::default::default,
    js::{any::Any, js_object::JsObjectRef},
    mem::{global::GLOBAL, manager::Manager},
    parser::{parse_with_tokens, Context, ParseError, ParseResult},
    tokenizer::tokenize,
};

use crate::{
    cdt::node_type::NodeType,
    common::status_line::{mb, StatusLine},
    forest::{file::FileForest, node_id::ForestNodeId, Forest},
    uint::u224::U224,
};

use super::{
    add::{directory_js, posix_path},
    get_hash, invalid_input, js_string_to_string, str_to_hash, try_move,
};

pub fn restore(
    io: &impl Io,
    hash: &U224,
    w: &mut impl Write,
    progress: &mut impl FnMut(u64, f64) -> io::Result<()>,
) -> io::Result<u64> {
    FileForest(io).restore(&ForestNodeId::new(NodeType::Root, hash), w, progress)
}

fn tokenize_and_parse<M: Manager>(
    io: &impl Io,
    manager: M,
    s: String,
) -> Result<ParseResult<M>, ParseError> {
    parse_with_tokens(
        &mut Context::new(manager, io, default(), &mut default()),
        tokenize(s).into_iter(),
    )
}

pub fn parse_json<M: Manager>(io: &impl Io, manager: M, v: Vec<u8>) -> io::Result<Any<M::Dealloc>> {
    let s = String::from_utf8(v).map_err(|_| invalid_input("Invalid UTF-8"))?;
    let result = tokenize_and_parse(io, manager, s).map_err(|_| invalid_input("Invalid JSON"))?;
    Ok(result.any)
}

fn dir(path: &str) -> Option<&str> {
    path.rsplit_once('/').map(|(d, _)| d)
}

fn create_file_path_recursively<T: Io>(io: &T, path: &str) -> io::Result<()> {
    dir(path).map(|d| io.create_dir_recursively(d));
    Ok(())
}

pub fn create_file_recursively<T: Io>(io: &T, path: &str) -> io::Result<T::File> {
    create_file_path_recursively(io, path)?;
    io.create(path)
}

fn set_progress(
    state: &mut StatusLine<impl Io>,
    progress_b: u64,
    progress_p: f64,
) -> io::Result<()> {
    state.set_progress(&(mb(progress_b) + ", "), progress_p)
}

fn get_if(d: &U224, path: &str, io: &impl Io) -> io::Result<()> {
    let mut state = StatusLine::new(io);
    if path.ends_with('/') {
        let mut buffer = Vec::default();
        let mut w = Cursor::new(&mut buffer);
        restore(io, d, &mut w, &mut |_, _| Ok(()))?;
        let json = try_move::<_, JsObjectRef<_>>(parse_json(io, GLOBAL, buffer)?)?;
        let dir = directory_js(GLOBAL);
        let dir_json = json
            .items()
            .iter()
            .find(|p| p.0.items() == dir.items())
            .ok_or(invalid_input("directory"))?;
        let dir_obj = try_move::<_, JsObjectRef<_>>(dir_json.1.clone())?;
        let items = dir_obj.items();
        let t = items.len();
        let mut b = 0;
        for (offset, (k, v)) in items.iter().enumerate() {
            let file = js_string_to_string(k)?;
            let hash = js_string_to_string(&try_move(v.clone())?)?;
            b += restore(
                io,
                &str_to_hash(&hash)?,
                &mut create_file_recursively(io, (path.to_owned() + &file).as_str())?,
                &mut |progress_b, progress_p| {
                    set_progress(
                        &mut state,
                        b + progress_b,
                        (offset as f64 + progress_p) / t as f64,
                    )
                },
            )?;
        }
        Ok(())
    } else {
        restore(
            io,
            d,
            &mut create_file_recursively(io, path)?,
            &mut |progress_b, progress_p| set_progress(&mut state, progress_b, progress_p),
        )?;
        Ok(())
    }
}

pub fn get<T: Io>(io: &T, a: &mut T::Args) -> io::Result<()> {
    get_if(
        &get_hash(a)?,
        &posix_path(a.next().ok_or(invalid_input("missing file name"))?.as_str()),
        io,
    )
}