Skip to main content

Tags

Struct Tags 

Source
pub struct Tags { /* private fields */ }
Expand description

Database manager for tag operations and task-tag relationships.

The Tags struct provides a high-level interface for managing tags and their associations with tasks. It handles database connections, schema initialization, and provides methods for all tag-related operations.

§Functionality

  • CRUD Operations: Create, read, update, and delete tag definitions
  • Relationship Management: Associate and disassociate tags with tasks
  • Batch Operations: Efficiently handle multiple tag operations
  • Query Support: Search and filter tags and their associations

§Database Schema Management

The struct automatically ensures that required database tables exist when instantiated, handling schema creation and migration compatibility.

Implementations§

Source§

impl Tags

Source

pub fn new() -> Result<Self>

Creates a new Tags manager and initializes the database schema.

This constructor establishes a database connection, ensures that the required tables exist, and prepares the manager for tag operations. Schema creation is idempotent and safe to call multiple times.

§Returns

Returns a new Tags instance ready for tag management operations, or an error if database initialization fails.

§Example
use kasl::db::tags::Tags;

let mut tags = Tags::new()?;
// Ready for tag operations
§Errors

Returns an error if:

  • Database connection cannot be established
  • Schema creation fails due to permissions or corruption
  • Migration system encounters errors during table setup
Source

pub fn create(&mut self, tag: &Tag) -> Result<i32>

Creates a new tag in the database and returns its assigned ID.

This method inserts a new tag record with the provided name and color, automatically assigning a unique ID and creation timestamp. Tag names must be unique across the system.

§Arguments
  • tag - Tag object containing name and optional color
§Returns

Returns the database-assigned ID for the new tag, or an error if creation fails (e.g., due to duplicate names).

§Example
use kasl::db::tags::{Tags, Tag};

let mut tags = Tags::new()?;
let tag = Tag::new("priority".to_string(), Some("orange".to_string()));
let tag_id = tags.create(&tag)?;
println!("Created tag with ID: {}", tag_id);
§Errors

Returns an error if:

  • A tag with the same name already exists
  • Database constraints are violated
  • Connection or transaction failures occur
Source

pub fn update(&mut self, tag: &Tag) -> Result<()>

Updates an existing tag’s name and color properties.

This method modifies an existing tag’s properties while preserving its ID, creation timestamp, and task associations. The updated name must still be unique across all tags.

§Arguments
  • tag - Tag object with updated properties (must have a valid ID)
§Returns

Returns Ok(()) if the update succeeds, or an error if the operation fails or the tag doesn’t exist.

§Example
let mut tags = Tags::new()?;
let tag_id = 1;
let mut tag = tags.get_by_id(tag_id)?.unwrap();
tag.name = "high-priority".to_string();
tag.color = Some("crimson".to_string());
tags.update(&tag)?;
§Errors

Returns an error if:

  • The tag ID doesn’t exist in the database
  • The new name conflicts with an existing tag
  • Database constraints are violated
Source

pub fn delete(&mut self, id: i32) -> Result<()>

Deletes a tag and all its task associations from the database.

This method permanently removes a tag definition and automatically cleans up all task-tag relationships through foreign key cascading. The operation is atomic and cannot be undone without database backups.

§Arguments
  • id - Unique identifier of the tag to delete
§Returns

Returns Ok(()) if deletion succeeds, or an error if the operation fails. Deleting a non-existent tag is not considered an error.

§Example
let mut tags = Tags::new()?;
let tag_id = 1;
tags.delete(tag_id)?; // Removes tag and all associations
§Side Effects
  • Removes the tag definition from the tags table
  • Automatically removes all task-tag associations via CASCADE
  • Cannot be undone without database restore operations
Source

pub fn get_all(&mut self) -> Result<Vec<Tag>>

Retrieves all tags from the database ordered alphabetically.

This method returns a complete list of all tag definitions sorted by name for consistent display in user interfaces and reports. The list includes all tag properties including colors and timestamps.

§Returns

Returns a vector of all tag records ordered by name, or an error if the database query fails.

§Example
let mut tags = Tags::new()?;
let all_tags = tags.get_all()?;
for tag in all_tags {
    println!("Tag: {} ({})", tag.name, tag.color.unwrap_or("no color".to_string()));
}
§Performance Considerations

This method loads all tags into memory, which is efficient for small to medium tag collections but may need pagination for very large datasets.

Source

pub fn get_by_name(&mut self, name: &str) -> Result<Option<Tag>>

Finds a tag by its unique name with case-sensitive matching.

This method performs an exact name lookup to find a specific tag definition. It’s commonly used for validation during tag creation and for resolving tag names to IDs in user commands.

§Arguments
  • name - Exact name of the tag to find (case-sensitive)
§Returns

Returns Some(Tag) if found, None if no matching tag exists, or an error if the database query fails.

§Example
let mut tags = Tags::new()?;
if let Some(tag) = tags.get_by_name("urgent")? {
    println!("Found tag: {} with color: {:?}", tag.name, tag.color);
} else {
    println!("Tag 'urgent' not found");
}
§Name Matching
  • Performs exact, case-sensitive string matching
  • Does not support wildcards or partial matching
  • Whitespace is significant and must match exactly
Source

pub fn get_by_id(&mut self, id: i32) -> Result<Option<Tag>>

Retrieves a tag by its unique database identifier.

This method provides direct access to tag details using the primary key for efficient lookups during task-tag association operations and when processing user commands with tag IDs.

§Arguments
  • id - Unique database identifier of the tag
§Returns

Returns Some(Tag) if found, None if the ID doesn’t exist, or an error if the database query fails.

§Example
let mut tags = Tags::new()?;
if let Some(tag) = tags.get_by_id(42)? {
    println!("Tag ID 42: {}", tag.name);
}
§Performance

This is the most efficient way to retrieve a tag when the ID is known, as it uses the primary key index for direct record access.

Source

pub fn get_tags_by_task(&mut self, task_id: i32) -> Result<Vec<Tag>>

Retrieves all tags associated with a specific task.

This method returns the complete set of tags linked to a task, ordered alphabetically for consistent display. It joins the tags and task_tags tables to provide full tag information.

§Arguments
  • task_id - Unique identifier of the task
§Returns

Returns a vector of tags associated with the task, or an error if the database query fails.

§Example
let mut tags = Tags::new()?;
let task_id = 1;
let task_tags = tags.get_tags_by_task(task_id)?;
for tag in task_tags {
    println!("Task has tag: {}", tag.name);
}
§Return Characteristics
  • Empty vector if the task has no tags
  • Results ordered alphabetically by tag name
  • Includes full tag details (name, color, timestamps)
Source

pub fn get_tasks_by_tag(&mut self, tag_id: i32) -> Result<Vec<i32>>

Finds all tasks associated with a specific tag.

This method returns the list of task IDs that have been tagged with the specified tag. It’s useful for tag-based filtering and generating reports of tasks by category.

§Arguments
  • tag_id - Unique identifier of the tag
§Returns

Returns a vector of task IDs associated with the tag, or an error if the database query fails.

§Example
let mut tags = Tags::new()?;
let tag_id = 1;
let task_ids = tags.get_tasks_by_tag(tag_id)?;
println!("Tag is used by {} tasks", task_ids.len());
§Use Cases
  • Tag-based task filtering in reports
  • Counting tag usage frequency
  • Validating tag deletion impact
  • Generating tag-specific task lists
Source

pub fn add_tag_to_task(&mut self, task_id: i32, tag_id: i32) -> Result<()>

Associates a tag with a task, creating a many-to-many relationship.

This method creates a link between a task and a tag in the junction table. If the association already exists, the operation succeeds without creating duplicates due to the OR IGNORE clause.

§Arguments
  • task_id - Unique identifier of the task
  • tag_id - Unique identifier of the tag
§Returns

Returns Ok(()) if the association is created or already exists, or an error if the database operation fails.

§Example
let mut tags = Tags::new()?;
let (task_id, tag_id) = (1, 1);
tags.add_tag_to_task(task_id, tag_id)?;
println!("Tag associated with task");
§Idempotency

This operation is idempotent - calling it multiple times with the same parameters has the same effect as calling it once.

Source

pub fn remove_tag_from_task(&mut self, task_id: i32, tag_id: i32) -> Result<()>

Removes a specific tag association from a task.

This method removes the link between a task and a tag without affecting the tag definition or other task associations. The operation is safe and succeeds even if the association doesn’t exist.

§Arguments
  • task_id - Unique identifier of the task
  • tag_id - Unique identifier of the tag to remove
§Returns

Returns Ok(()) if the removal succeeds, or an error if the database operation fails.

§Example
let mut tags = Tags::new()?;
let (task_id, tag_id) = (1, 1);
tags.remove_tag_from_task(task_id, tag_id)?;
println!("Tag removed from task");
§Side Effects
  • Only affects the specific task-tag relationship
  • Does not delete the tag or task definitions
  • Safe to call even if the association doesn’t exist
Source

pub fn remove_all_tags_from_task(&mut self, task_id: i32) -> Result<usize>

Removes all tag associations from a specific task.

This method clears all tags from a task, effectively resetting its tag assignments. It’s commonly used when deleting tasks or when users want to completely re-tag a task.

§Arguments
  • task_id - Unique identifier of the task to clear
§Returns

Returns the number of associations removed, or an error if the database operation fails.

§Example
let mut tags = Tags::new()?;
let task_id = 1;
let removed_count = tags.remove_all_tags_from_task(task_id)?;
println!("Removed {} tag associations", removed_count);
§Use Cases
  • Task deletion cleanup
  • Bulk tag reassignment workflows
  • Resetting task categorization
  • Data migration and correction operations
Source

pub fn set_task_tags(&mut self, task_id: i32, tag_ids: &[i32]) -> Result<()>

Replaces all tag associations for a task with a new set of tags.

This method provides atomic tag assignment by completely replacing a task’s current tag associations with a new set. It ensures that the task ends up with exactly the specified tags, regardless of its previous tag state.

§Operation Sequence

The method performs a two-step atomic operation:

  1. Clear Existing: Removes all current tag associations for the task
  2. Add New: Creates associations for each specified tag ID

This approach ensures consistency and prevents partial update states that could occur with manual add/remove operations.

§Transaction Semantics

While not explicitly wrapped in a transaction, the operation is designed to be atomic - either all associations are updated successfully or the task retains its original tag state if any error occurs.

§Use Cases
  • Bulk Tag Assignment: Assigning multiple tags to a task at once
  • Tag Replacement: Completely changing a task’s tag categorization
  • Import Operations: Setting tags during data import or migration
  • Template Application: Applying predefined tag sets from templates
  • UI Operations: Saving tag selections from multi-select interfaces
§Arguments
  • task_id - Unique identifier of the task to update
  • tag_ids - Slice of tag IDs to associate with the task
§Returns

Returns Ok(()) if all tag associations are updated successfully, or an error if any database operation fails.

§Example
let mut tags = Tags::new()?;
let task_id = 1;

// Replace task tags with new set
let new_tag_ids = vec![1, 3, 5]; // urgent, backend, review
tags.set_task_tags(task_id, &new_tag_ids)?;

// Task now has exactly these three tags
let current_tags = tags.get_tags_by_task(task_id)?;
assert_eq!(current_tags.len(), 3);
§Performance Considerations
  • Efficient for Large Changes: More efficient than individual add/remove operations
  • Database Operations: Minimizes database round-trips through batch processing
  • Index Usage: Leverages database indices for both clear and insert operations
  • Memory Usage: Tag ID slice is processed iteratively to minimize memory usage
§Error Handling

If the clear operation succeeds but adding new tags fails, the task will be left with no tags. Callers should be prepared to handle this scenario and potentially retry the operation or restore previous state.

§Data Integrity

The method assumes all provided tag IDs exist in the database. Non-existent tag IDs will cause foreign key constraint violations and operation failure. Use get_or_create_tags() if tag existence is uncertain.

Source

pub fn get_or_create_tags(&mut self, names: &[String]) -> Result<Vec<i32>>

Creates or retrieves tags by name, returning their database IDs.

This convenience method handles the common workflow of ensuring tags exist before associating them with tasks. For each provided name, it either returns the existing tag ID or creates a new tag with a default color.

§Batch Processing

The method processes multiple tag names efficiently, checking for existence before creation and returning a complete list of IDs ready for task association.

§Arguments
  • names - Slice of tag names to create or retrieve
§Returns

Returns a vector of tag IDs corresponding to the input names, or an error if any database operation fails.

§Example
let mut tags = Tags::new()?;
let tag_names = vec!["urgent".to_string(), "backend".to_string()];
let tag_ids = tags.get_or_create_tags(&tag_names)?;
// tag_ids now contains IDs for both tags (created if needed)
§Default Color Assignment

New tags are assigned colors from a predefined rotation to ensure visual variety. The color assignment is deterministic but varies across different tag creation sessions.

Auto Trait Implementations§

§

impl !Freeze for Tags

§

impl !RefUnwindSafe for Tags

§

impl !Sync for Tags

§

impl !UnwindSafe for Tags

§

impl Send for Tags

§

impl Unpin for Tags

§

impl UnsafeUnpin for Tags

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> PolicyExt for T
where T: ?Sized,

Source§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow only if self and other return Action::Follow. Read more
Source§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow if either self or other returns Action::Follow. Read more
Source§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more