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
impl Tags
Sourcepub fn new() -> Result<Self>
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
Sourcepub fn create(&mut self, tag: &Tag) -> Result<i32>
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
Sourcepub fn update(&mut self, tag: &Tag) -> Result<()>
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
Sourcepub fn delete(&mut self, id: i32) -> Result<()>
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
Sourcepub fn get_all(&mut self) -> Result<Vec<Tag>>
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.
Sourcepub fn get_by_name(&mut self, name: &str) -> Result<Option<Tag>>
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
Sourcepub fn get_by_id(&mut self, id: i32) -> Result<Option<Tag>>
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.
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)
Sourcepub fn get_tasks_by_tag(&mut self, tag_id: i32) -> Result<Vec<i32>>
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
Sourcepub fn add_tag_to_task(&mut self, task_id: i32, tag_id: i32) -> Result<()>
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 tasktag_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.
Sourcepub fn remove_tag_from_task(&mut self, task_id: i32, tag_id: i32) -> Result<()>
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 tasktag_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
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
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:
- Clear Existing: Removes all current tag associations for the task
- 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 updatetag_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.
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.