~calmbit/suki

e7a75e5c70fee6707ee678366f8a123c7eb59afe — CalmBit 6 months ago ba11875
subject myself to linting
3 files changed, 33 insertions(+), 27 deletions(-)

M Cargo.toml
M src/main.rs
M src/suki.rs
M Cargo.toml => Cargo.toml +7 -0
@@ 3,6 3,13 @@ name = "suki"
version = "0.1.0"
authors = ["CalmBit <EB5473@gmail.com>"]
edition = "2018"
description = "suki - the simple unique krap itemizer"
license = "MIT"
license_file = "LICENSE"
repository = "https://git.sr.ht/~calmbit/suki"
readme = "README.md"
keywords = ["tagging", "files"]
categories = ["tools"]

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html


M src/main.rs => src/main.rs +7 -5
@@ 1,4 1,6 @@
const VERSION: &'static str = env!("CARGO_PKG_VERSION");
#![warn(clippy::pedantic)]

const VERSION: &str = env!("CARGO_PKG_VERSION");

mod suki;



@@ 46,7 48,7 @@ fn main() -> Result<(), String> {
    match cmd.as_ref() {
        "t" | "tag" => tag(&filename, args.split_at(current_arg).1, &flags),
        "r" | "remove" => remove(&filename, args.split_at(current_arg).1, &flags),
        "s" | "search" => Err(format!("unimplemented cmd - search")),
        "s" | "search" => Err(String::from("unimplemented cmd - search")),
        "h" | "help" => {
            print_help();
            Ok(())


@@ 67,7 69,7 @@ fn tag(filename: &str, tags: &[String], flags: &[Flags]) -> Result<(), String> {
        eprintln!("file: {}, tags: {:?}", filename, tags);
    }

    let mut file = suki::SukiFile::new(&dir)?;
    let mut file = suki::File::new(&dir)?;

    if !tags.is_empty() {
        for t in tags {


@@ 80,7 82,7 @@ fn tag(filename: &str, tags: &[String], flags: &[Flags]) -> Result<(), String> {
                }
            } 
            if !found {
                let mut new_tag = suki::SukiTag::new(t);
                let mut new_tag = suki::Tag::new(t);
                new_tag.files.push(String::from(filename));
                file.tags.push(new_tag);
            }


@@ 95,7 97,7 @@ fn remove(filename: &str, tags: &[String], flags: &[Flags]) -> Result<(), String
    if flags.contains(&Flags::Debug) {
        println!("file: {}, tags: {:?}", filename, tags);
    }
    let mut file = suki::SukiFile::new(&dir)?;
    let mut file = suki::File::new(&dir)?;

    if !tags.is_empty() {
        for t in tags {

M src/suki.rs => src/suki.rs +19 -22
@@ 1,31 1,31 @@
use std::error::Error;
use std::io::{Read, Write};

/// SukiTags represent the tag -> filename collections that are contained by
/// SukiFiles.
/// `Tags` represent the tag -> filename collections that are contained by
/// `Files`.
#[derive(std::fmt::Debug)]
pub struct SukiTag {
pub struct Tag {
    pub tag: String,
    pub files: Vec<String>,
}

/// SukiFiles are the primary operative component of suki, encapsulating all of
/// `Files` are the primary operative component of suki, encapsulating all of
/// the tag -> filename relationships that `.suki` files encode for.
#[derive(std::fmt::Debug)]
pub struct SukiFile {
    pub tags: Vec<SukiTag>,
pub struct File {
    pub tags: Vec<Tag>,
}

impl SukiTag {
    pub fn new(tag: &str) -> SukiTag {
        SukiTag {
impl Tag {
    pub fn new(tag: &str) -> Self {
        Self {
            tag: String::from(tag),
            files: Vec::new(),
        }
    }
}

impl SukiFile {
impl File {
    pub fn serialize(self, path: &str) -> Result<(), String> {
        let mut file = match open_and_clear_suki(path) {
            Ok(f) => f,


@@ 50,8 50,8 @@ impl SukiFile {
        Ok(())
    }

    pub fn new(path: &str) -> Result<SukiFile, String> {
        let mut file_buffer = SukiFile { tags: Vec::new() };
    pub fn new(path: &str) -> Result<Self, String> {
        let mut file_buffer = Self { tags: Vec::new() };

        let mut txt_file = match open_suki(path) {
            Ok(f) => f,


@@ 64,17 64,15 @@ impl SukiFile {
            Err(e) => return Err(String::from(e.description())),
        }

        let mut line_no = 0;
        let mut tag_buffer: Option<SukiTag> = Option::None;
        for l in buf.split_terminator('\n') {
            line_no += 1;
        let mut tag_buffer: Option<Tag> = Option::None;
        for (line_no, l) in buf.split_terminator('\n').enumerate() {
            // Filenames are delimited with '\t', the tab character. This is
            // the primary trait that enforces a hierarchy of tags 1..* files.
            if l.starts_with('\t') {
                match tag_buffer.as_mut() {
                    Some(s) => s.files.push(String::from(&l[1..])),
                    None => {
                        return Err(format!("bad syntax - cannot start suki file with filename"))
                        return Err(String::from("bad syntax - cannot start suki file with filename"))
                    }
                }
                continue;


@@ 88,13 86,12 @@ impl SukiFile {
            if l.ends_with(':') {
                // If we've already got a compiled tag on our plate, push it to
                // the file and make room for the new tag.
                match tag_buffer {
                    Some(s) => file_buffer.tags.push(s),
                    None => (),
                if let Some(s) = tag_buffer {
                    file_buffer.tags.push(s)
                }
                // Set up the brand new tag with its label, and an empty vector
                // for its filenames.
                tag_buffer = Some(SukiTag::new(
                tag_buffer = Some(Tag::new(
                    // Right here we cut off the colon at the end of the line.
                    &l[..l.len() - 1],
                ));


@@ 104,7 101,7 @@ impl SukiFile {
            // TODO: This is arbitrary. Pending removal/change of message?
            return Err(format!(
                "bad syntax at line {} - missing ':' at end of label descriptor",
                line_no
                line_no+1
            ));
        }