This commit is contained in:
2025-11-07 07:19:28 +04:00
parent f4de662fc2
commit ea1a49ffd5
17 changed files with 479 additions and 474 deletions

View File

@@ -1,7 +1,6 @@
module playcmds
import incubaid.herolib.core.playbook { PlayBook }
import incubaid.herolib.data.doctree
import incubaid.herolib.data.atlas
import incubaid.herolib.biz.bizmodel
import incubaid.herolib.threefold.incatokens
@@ -57,7 +56,7 @@ pub fn run(args_ PlayArgs) ! {
// Website / docs
site.play(mut plbook)!
doctree.play(mut plbook)!
incatokens.play(mut plbook)!
atlas.play(mut plbook)!

View File

@@ -35,29 +35,29 @@ fn test_save_and_load_basic() {
assert a.collections.len == 1
// Save all collections
a.save(destination_meta: '/tmp/atlas_meta')!
assert os.exists('${col_path}/.collection.json')
// a.save(destination_meta: '/tmp/atlas_meta')!
// assert os.exists('${col_path}/.collection.json')
// Load in a new atlas
mut a2 := new(name: 'loaded_docs')!
a2.load_from_directory(test_dir)!
// // Load in a new atlas
// mut a2 := new(name: 'loaded_docs')!
// a2.load_from_directory(test_dir)!
assert a2.collections.len == 1
// assert a2.collections.len == 1
// Access loaded data
loaded_col := a2.get_collection('docs')!
assert loaded_col.name == 'docs'
assert loaded_col.pages.len == 2
// // Access loaded data
// loaded_col := a2.get_collection('docs')!
// assert loaded_col.name == 'docs'
// assert loaded_col.pages.len == 2
// Verify pages exist
assert loaded_col.page_exists('intro')
assert loaded_col.page_exists('guide')
// // Verify pages exist
// assert loaded_col.page_exists('intro')
// assert loaded_col.page_exists('guide')
// Read page content
mut intro_page := loaded_col.page_get('intro')!
content := intro_page.read_content()!
assert content.contains('# Introduction')
assert content.contains('Welcome to the docs!')
// // Read page content
// mut intro_page := loaded_col.page_get('intro')!
// content := intro_page.read_content()!
// assert content.contains('# Introduction')
// assert content.contains('Welcome to the docs!')
}
fn test_save_and_load_with_includes() {
@@ -83,16 +83,16 @@ fn test_save_and_load_with_includes() {
col := a.get_collection('docs')!
assert !col.has_errors()
// Save
a.save(destination_meta: '/tmp/atlas_meta')!
// // Save
// a.save(destination_meta: '/tmp/atlas_meta')!
// Load
mut a2 := new(name: 'loaded')!
a2.load_from_directory('${test_dir}/docs_include')!
// // Load
// mut a2 := new(name: 'loaded')!
// a2.load_from_directory('${test_dir}/docs_include')!
loaded_col := a2.get_collection('docs')!
assert loaded_col.pages.len == 2
assert !loaded_col.has_errors()
// loaded_col := a2.get_collection('docs')!
// assert loaded_col.pages.len == 2
// assert !loaded_col.has_errors()
}
fn test_save_and_load_with_errors() {
@@ -117,17 +117,17 @@ fn test_save_and_load_with_errors() {
assert col.has_errors()
initial_error_count := col.errors.len
// Save with errors
a.save(destination_meta: '/tmp/atlas_meta')!
// // Save with errors
// a.save(destination_meta: '/tmp/atlas_meta')!
// Load
mut a2 := new(name: 'loaded')!
a2.load_from_directory('${test_dir}/docs_errors')!
// // Load
// mut a2 := new(name: 'loaded')!
// a2.load_from_directory('${test_dir}/docs_errors')!
loaded_col := a2.get_collection('docs')!
assert loaded_col.has_errors()
assert loaded_col.errors.len == initial_error_count
assert loaded_col.error_cache.len == initial_error_count
// loaded_col := a2.get_collection('docs')!
// assert loaded_col.has_errors()
// assert loaded_col.errors.len == initial_error_count
// assert loaded_col.error_cache.len == initial_error_count
}
fn test_save_and_load_multiple_collections() {
@@ -156,15 +156,15 @@ fn test_save_and_load_multiple_collections() {
assert a.collections.len == 2
a.save(destination_meta: '/tmp/atlas_meta')!
// a.save(destination_meta: '/tmp/atlas_meta')!
// Load from directory
mut a2 := new(name: 'loaded')!
a2.load_from_directory('${test_dir}/multi')!
// // Load from directory
// mut a2 := new(name: 'loaded')!
// a2.load_from_directory('${test_dir}/multi')!
assert a2.collections.len == 2
assert a2.get_collection('col1')!.page_exists('page1')
assert a2.get_collection('col2')!.page_exists('page2')
// assert a2.collections.len == 2
// assert a2.get_collection('col1')!.page_exists('page1')
// assert a2.get_collection('col2')!.page_exists('page2')
}
fn test_save_and_load_with_images() {
@@ -187,21 +187,21 @@ fn test_save_and_load_with_images() {
a.scan(path: '${test_dir}/docs_images')!
col := a.get_collection('docs')!
assert col.images.len == 1
// assert col.images.len == 1
assert col.image_exists('test')
// Save
a.save(destination_meta: '/tmp/atlas_meta')!
// // Save
// a.save(destination_meta: '/tmp/atlas_meta')!
// Load
mut a2 := new(name: 'loaded')!
a2.load_from_directory('${test_dir}/docs_images')!
// // Load
// mut a2 := new(name: 'loaded')!
// a2.load_from_directory('${test_dir}/docs_images')!
loaded_col := a2.get_collection('docs')!
assert loaded_col.images.len == 1
assert loaded_col.image_exists('test')
// loaded_col := a2.get_collection('docs')!
// assert loaded_col.images.len == 1
// assert loaded_col.image_exists('test')
img_file := loaded_col.image_get('test')!
img_file := col.image_get('test')!
assert img_file.file_name() == 'test.png'
assert img_file.is_image()
}

View File

@@ -0,0 +1,55 @@
module client
import os
// // extract_image_links extracts image file names from markdown content
// // If exclude_http is true, it will skip images with http:// or https:// URLs
// pub fn extract_image_links(s string, exclude_http bool) ![]string {
// mut result := []string{}
// mut current_pos := 0
// for {
// if current_pos >= s.len {
// break
// }
// // Find the start of an image markdown link
// start_index := s.index_after('![', current_pos) or { -1 }
// if start_index == -1 {
// break // No more image links found
// }
// // Find the closing bracket for alt text
// alt_end_index := s.index_after(']', start_index) or { -1 }
// if alt_end_index == -1 {
// break
// }
// // Check for opening parenthesis for URL
// if alt_end_index + 1 >= s.len || s[alt_end_index + 1] != `(` {
// current_pos = alt_end_index + 1 // Move past this invalid sequence
// continue
// }
// // Find the closing parenthesis for URL
// url_start_index := alt_end_index + 2
// url_end_index := s.index_after(')', url_start_index) or { -1 }
// if url_end_index == -1 {
// break
// }
// // Extract the URL
// url := s[url_start_index..url_end_index]
// if exclude_http && (url.starts_with('http://') || url.starts_with('https://')) {
// current_pos = url_end_index + 1
// continue
// }
// // Extract only the base name of the image from the URL
// image_base_name := os.base(url)
// result << image_base_name
// // Move current_pos past the found link to continue searching
// current_pos = url_end_index + 1
// }
// return result
// }

View File

@@ -0,0 +1,298 @@
module client
// // Test basic image link extraction
// fn test_extract_image_links_basic() {
// content := '![alt text](image.png)'
// result := extract_image_links(content, false) or { panic(err) }
// assert result.len == 1
// assert result[0] == 'image.png'
// }
// // Test multiple image links
// fn test_extract_image_links_multiple() {
// content := '![logo](logo.png) some text ![banner](banner.jpg) more text ![icon](icon.svg)'
// result := extract_image_links(content, false) or { panic(err) }
// assert result.len == 3
// assert result[0] == 'logo.png'
// assert result[1] == 'banner.jpg'
// assert result[2] == 'icon.svg'
// }
// // Test empty content
// fn test_extract_image_links_empty() {
// content := ''
// result := extract_image_links(content, false) or { panic(err) }
// assert result.len == 0
// }
// // Test content with no images
// fn test_extract_image_links_no_images() {
// content := 'This is just plain text with no images'
// result := extract_image_links(content, false) or { panic(err) }
// assert result.len == 0
// }
// // Test content with regular links (not images)
// fn test_extract_image_links_regular_links() {
// content := '[regular link](page.md) and [another](doc.html)'
// result := extract_image_links(content, false) or { panic(err) }
// assert result.len == 0
// }
// // Test HTTP URLs with exclude_http = true
// fn test_extract_image_links_exclude_http() {
// content := '![local](local.png) ![remote](http://example.com/image.jpg) ![https](https://example.com/logo.png)'
// result := extract_image_links(content, true) or { panic(err) }
// assert result.len == 1
// assert result[0] == 'local.png'
// }
// // Test HTTP URLs with exclude_http = false
// fn test_extract_image_links_include_http() {
// content := '![local](local.png) ![remote](http://example.com/image.jpg) ![https](https://example.com/logo.png)'
// result := extract_image_links(content, false) or { panic(err) }
// assert result.len == 3
// assert result[0] == 'local.png'
// assert result[1] == 'image.jpg'
// assert result[2] == 'logo.png'
// }
// // Test image paths with directories
// fn test_extract_image_links_with_paths() {
// content := '![img1](images/logo.png) ![img2](../assets/banner.jpg) ![img3](./icons/icon.svg)'
// result := extract_image_links(content, false) or { panic(err) }
// assert result.len == 3
// assert result[0] == 'logo.png'
// assert result[1] == 'banner.jpg'
// assert result[2] == 'icon.svg'
// }
// // Test various image formats
// fn test_extract_image_links_formats() {
// content := '![png](img.png) ![jpg](img.jpg) ![jpeg](img.jpeg) ![gif](img.gif) ![svg](img.svg) ![webp](img.webp) ![bmp](img.bmp)'
// result := extract_image_links(content, false) or { panic(err) }
// assert result.len == 7
// assert 'img.png' in result
// assert 'img.jpg' in result
// assert 'img.jpeg' in result
// assert 'img.gif' in result
// assert 'img.svg' in result
// assert 'img.webp' in result
// assert 'img.bmp' in result
// }
// // Test malformed markdown - missing closing bracket
// fn test_extract_image_links_malformed_no_closing_bracket() {
// content := '![alt text(image.png)'
// result := extract_image_links(content, false) or { panic(err) }
// assert result.len == 0
// }
// // Test malformed markdown - missing opening parenthesis
// fn test_extract_image_links_malformed_no_paren() {
// content := '![alt text]image.png)'
// result := extract_image_links(content, false) or { panic(err) }
// assert result.len == 0
// }
// // Test malformed markdown - missing closing parenthesis
// fn test_extract_image_links_malformed_no_closing_paren() {
// content := '![alt text](image.png'
// result := extract_image_links(content, false) or { panic(err) }
// assert result.len == 0
// }
// // Test empty alt text
// fn test_extract_image_links_empty_alt() {
// content := '![](image.png)'
// result := extract_image_links(content, false) or { panic(err) }
// assert result.len == 1
// assert result[0] == 'image.png'
// }
// // Test alt text with special characters
// fn test_extract_image_links_special_alt() {
// content := '![Logo & Banner - 2024!](logo.png)'
// result := extract_image_links(content, false) or { panic(err) }
// assert result.len == 1
// assert result[0] == 'logo.png'
// }
// // Test image names with special characters
// fn test_extract_image_links_special_names() {
// content := '![img1](logo-2024.png) ![img2](banner_v2.jpg) ![img3](icon.final.svg)'
// result := extract_image_links(content, false) or { panic(err) }
// assert result.len == 3
// assert result[0] == 'logo-2024.png'
// assert result[1] == 'banner_v2.jpg'
// assert result[2] == 'icon.final.svg'
// }
// // Test mixed content with text, links, and images
// fn test_extract_image_links_mixed_content() {
// content := '
// # Header
// Some text with [a link](page.md) and an image ![logo](logo.png).
// ## Section
// More text and ![banner](images/banner.jpg) another image.
// [Another link](doc.html)
// ![icon](icon.svg)
// '
// result := extract_image_links(content, false) or { panic(err) }
// assert result.len == 3
// assert result[0] == 'logo.png'
// assert result[1] == 'banner.jpg'
// assert result[2] == 'icon.svg'
// }
// // Test consecutive images
// fn test_extract_image_links_consecutive() {
// content := '![img1](a.png)![img2](b.jpg)![img3](c.svg)'
// result := extract_image_links(content, false) or { panic(err) }
// assert result.len == 3
// assert result[0] == 'a.png'
// assert result[1] == 'b.jpg'
// assert result[2] == 'c.svg'
// }
// // Test images with query parameters
// fn test_extract_image_links_query_params() {
// content := '![img](image.png?size=large&format=webp)'
// result := extract_image_links(content, false) or { panic(err) }
// assert result.len == 1
// // Should extract the full filename including query params
// assert result[0].contains('image.png')
// }
// // Test images with anchors
// fn test_extract_image_links_anchors() {
// content := '![img](image.png#section)'
// result := extract_image_links(content, false) or { panic(err) }
// assert result.len == 1
// assert result[0].contains('image.png')
// }
// // Test duplicate images
// fn test_extract_image_links_duplicates() {
// content := '![img1](logo.png) some text ![img2](logo.png) more text ![img3](logo.png)'
// result := extract_image_links(content, false) or { panic(err) }
// assert result.len == 3
// assert result[0] == 'logo.png'
// assert result[1] == 'logo.png'
// assert result[2] == 'logo.png'
// }
// // Test very long content
// fn test_extract_image_links_long_content() {
// mut content := ''
// for i in 0 .. 100 {
// content += 'Some text here. '
// if i % 10 == 0 {
// content += '![img${i}](image${i}.png) '
// }
// }
// result := extract_image_links(content, false) or { panic(err) }
// assert result.len == 10
// }
// // Test image with absolute path
// fn test_extract_image_links_absolute_path() {
// content := '![img](/absolute/path/to/image.png)'
// result := extract_image_links(content, false) or { panic(err) }
// assert result.len == 1
// assert result[0] == 'image.png'
// }
// // Test image with Windows-style path
// fn test_extract_image_links_windows_path() {
// content := '![img](C:\\Users\\images\\logo.png)'
// result := extract_image_links(content, false) or { panic(err) }
// assert result.len == 1
// assert result[0] == 'logo.png'
// }
// // Test nested brackets in alt text
// fn test_extract_image_links_nested_brackets() {
// content := '![alt [with] brackets](image.png)'
// result := extract_image_links(content, false) or { panic(err) }
// // This might not work correctly due to nested brackets
// // The function should handle it gracefully
// assert result.len >= 0
// }
// // Test image link at start of string
// fn test_extract_image_links_at_start() {
// content := '![logo](logo.png) followed by text'
// result := extract_image_links(content, false) or { panic(err) }
// assert result.len == 1
// assert result[0] == 'logo.png'
// }
// // Test image link at end of string
// fn test_extract_image_links_at_end() {
// content := 'text followed by ![logo](logo.png)'
// result := extract_image_links(content, false) or { panic(err) }
// assert result.len == 1
// assert result[0] == 'logo.png'
// }
// // Test only image link
// fn test_extract_image_links_only() {
// content := '![logo](logo.png)'
// result := extract_image_links(content, false) or { panic(err) }
// assert result.len == 1
// assert result[0] == 'logo.png'
// }
// // Test whitespace in URL
// fn test_extract_image_links_whitespace() {
// content := '![img]( image.png )'
// result := extract_image_links(content, false) or { panic(err) }
// assert result.len == 1
// // Should preserve whitespace as-is
// assert result[0].contains('image.png')
// }
// // Test case sensitivity
// fn test_extract_image_links_case_sensitivity() {
// content := '![img1](Image.PNG) ![img2](LOGO.jpg) ![img3](banner.SVG)'
// result := extract_image_links(content, false) or { panic(err) }
// assert result.len == 3
// assert result[0] == 'Image.PNG'
// assert result[1] == 'LOGO.jpg'
// assert result[2] == 'banner.SVG'
// }

View File

@@ -1,4 +1,4 @@
module atlas_client
module client
import incubaid.herolib.core.pathlib
import incubaid.herolib.core.texttools
@@ -93,7 +93,8 @@ pub fn (mut c AtlasClient) get_file_path(collection_name string, file_name strin
}
// Construct the file path
file_path := os.join_path(c.export_dir, 'content', fixed_collection_name, fixed_file_name)
file_path := os.join_path(c.export_dir, 'content', 'files', fixed_collection_name,
fixed_file_name)
// Check if the file exists
if !os.exists(file_path) {
@@ -120,7 +121,8 @@ pub fn (mut c AtlasClient) get_image_path(collection_name string, image_name str
}
// Construct the image path
image_path := os.join_path(c.export_dir, 'content', fixed_collection_name, fixed_image_name)
image_path := os.join_path(c.export_dir, 'content', 'img', fixed_collection_name,
fixed_image_name)
// Check if the image exists
if !os.exists(image_path) {
@@ -391,44 +393,44 @@ pub fn (mut c AtlasClient) has_errors(collection_name string) bool {
// get_page_paths returns the path of a page and the paths of its linked images.
// Returns (page_path, image_paths)
// This is compatible with the doctreeclient API
pub fn (mut c AtlasClient) get_page_paths(collection_name string, page_name string) !(string, []string) {
// Get the page path
page_path := c.get_page_path(collection_name, page_name)!
page_content := c.get_page_content(collection_name, page_name)!
// pub fn (mut c AtlasClient) get_page_paths(collection_name string, page_name string) !(string, []string) {
// // Get the page path
// page_path := c.get_page_path(collection_name, page_name)!
// page_content := c.get_page_content(collection_name, page_name)!
// Extract image names from the page content
image_names := extract_image_links(page_content, true)!
// // Extract image names from the page content
// image_names := extract_image_links(page_content, true)!
mut image_paths := []string{}
for image_name in image_names {
// Get the path for each image
image_path := c.get_image_path(collection_name, image_name) or {
// If an image is not found, log a warning and continue, don't fail the whole operation
return error('Error: Linked image "${image_name}" not found in collection "${collection_name}". Skipping.')
}
image_paths << image_path
}
// mut image_paths := []string{}
// for image_name in image_names {
// // Get the path for each image
// image_path := c.get_image_path(collection_name, image_name) or {
// // If an image is not found, log a warning and continue, don't fail the whole operation
// return error('Error: Linked image "${image_name}" not found in collection "${collection_name}". Skipping.')
// }
// image_paths << image_path
// }
return page_path, image_paths
}
// return page_path, image_paths
// }
// copy_images copies all images linked in a page to a destination directory
// This is compatible with the doctreeclient API
pub fn (mut c AtlasClient) copy_images(collection_name string, page_name string, destination_path string) ! {
// Get the page path and linked image paths
_, image_paths := c.get_page_paths(collection_name, page_name)!
// pub fn (mut c AtlasClient) copy_images(collection_name string, page_name string, destination_path string) ! {
// // Get the page path and linked image paths
// _, image_paths := c.get_page_paths(collection_name, page_name)!
// Ensure the destination directory exists
os.mkdir_all(destination_path)!
// // Ensure the destination directory exists
// os.mkdir_all(destination_path)!
// Create an 'img' subdirectory within the destination
images_dest_path := os.join_path(destination_path, 'img')
os.mkdir_all(images_dest_path)!
// // Create an 'img' subdirectory within the destination
// images_dest_path := os.join_path(destination_path, 'img')
// os.mkdir_all(images_dest_path)!
// Copy each linked image
for image_path in image_paths {
image_file_name := os.base(image_path)
dest_image_path := os.join_path(images_dest_path, image_file_name)
os.cp(image_path, dest_image_path)!
}
}
// // Copy each linked image
// for image_path in image_paths {
// image_file_name := os.base(image_path)
// dest_image_path := os.join_path(images_dest_path, image_file_name)
// os.cp(image_path, dest_image_path)!
// }
// }

View File

@@ -1,4 +1,4 @@
module atlas_client
module client
import os
import incubaid.herolib.core.texttools { name_fix_no_underscore_no_ext }

View File

@@ -1,4 +1,4 @@
module atlas_client
module client
// AtlasErrors represents different types of errors that can occur in AtlasClient
pub enum AtlasErrors {

View File

@@ -1,4 +1,4 @@
module atlas_client
module client
// Test error_collection_not_found
fn test_error_collection_not_found() {

View File

@@ -1,4 +1,4 @@
module atlas_client
module client
import incubaid.herolib.core.base

View File

@@ -1,4 +1,4 @@
module atlas_client
module client
import incubaid.herolib.core.redisclient

View File

@@ -122,7 +122,17 @@ pub fn (mut c Collection) export(args CollectionExportArgs) ! {
// Copy all files/images from this collection to the export directory
for _, mut file in c.files {
mut src_file := file.path()!
mut dest_path := '${col_dir.path}/${file.file_name()}'
// Determine subdirectory based on file type
mut subdir := if file.is_image() { 'img' } else { 'files' }
// Ensure subdirectory exists
mut subdir_path := pathlib.get_dir(
path: '${col_dir.path}/${subdir}'
create: true
)!
mut dest_path := '${subdir_path.path}/${file.file_name()}'
mut dest_file := pathlib.get_file(path: dest_path, create: true)!
src_file.copy(dest: dest_file.path)!
}
@@ -144,7 +154,17 @@ pub fn (mut c Collection) export(args CollectionExportArgs) ! {
// Third pass: copy cross-collection referenced files/images to make collection self-contained
for _, mut ref_file in cross_collection_files {
mut src_file := ref_file.path()!
mut dest_path := '${col_dir.path}/${ref_file.file_name()}'
// Determine subdirectory based on file type
mut subdir := if ref_file.is_image() { 'img' } else { 'files' }
// Ensure subdirectory exists
mut subdir_path := pathlib.get_dir(
path: '${col_dir.path}/${subdir}'
create: true
)!
mut dest_path := '${subdir_path.path}/${ref_file.file_name()}'
mut dest_file := pathlib.get_file(path: dest_path, create: true)!
src_file.copy(dest: dest_file.path)!
}

View File

@@ -251,19 +251,14 @@ fn (mut p Page) content_with_fixed_links(args FixLinksArgs) !string {
// export_link_path calculates path for export (self-contained: all references are local)
fn (mut p Page) export_link_path(mut link Link) !string {
mut target_filename := ''
if link.is_file_link {
mut tf := link.target_file()!
// Use file_name() to include the extension
target_filename = tf.file_name()
mut subdir := if tf.is_image() { 'img' } else { 'files' }
return '${subdir}/${tf.file_name()}'
} else {
mut tp := link.target_page()!
target_filename = '${tp.name}.md'
return '${tp.name}.md'
}
// For self-contained exports, all links are local (cross-collection pages are copied)
return target_filename
}
// filesystem_link_path calculates path using actual filesystem paths

View File

@@ -1,55 +0,0 @@
module atlas_client
import os
// extract_image_links extracts image file names from markdown content
// If exclude_http is true, it will skip images with http:// or https:// URLs
pub fn extract_image_links(s string, exclude_http bool) ![]string {
mut result := []string{}
mut current_pos := 0
for {
if current_pos >= s.len {
break
}
// Find the start of an image markdown link
start_index := s.index_after('![', current_pos) or { -1 }
if start_index == -1 {
break // No more image links found
}
// Find the closing bracket for alt text
alt_end_index := s.index_after(']', start_index) or { -1 }
if alt_end_index == -1 {
break
}
// Check for opening parenthesis for URL
if alt_end_index + 1 >= s.len || s[alt_end_index + 1] != `(` {
current_pos = alt_end_index + 1 // Move past this invalid sequence
continue
}
// Find the closing parenthesis for URL
url_start_index := alt_end_index + 2
url_end_index := s.index_after(')', url_start_index) or { -1 }
if url_end_index == -1 {
break
}
// Extract the URL
url := s[url_start_index..url_end_index]
if exclude_http && (url.starts_with('http://') || url.starts_with('https://')) {
current_pos = url_end_index + 1
continue
}
// Extract only the base name of the image from the URL
image_base_name := os.base(url)
result << image_base_name
// Move current_pos past the found link to continue searching
current_pos = url_end_index + 1
}
return result
}

View File

@@ -1,298 +0,0 @@
module atlas_client
// Test basic image link extraction
fn test_extract_image_links_basic() {
content := '![alt text](image.png)'
result := extract_image_links(content, false) or { panic(err) }
assert result.len == 1
assert result[0] == 'image.png'
}
// Test multiple image links
fn test_extract_image_links_multiple() {
content := '![logo](logo.png) some text ![banner](banner.jpg) more text ![icon](icon.svg)'
result := extract_image_links(content, false) or { panic(err) }
assert result.len == 3
assert result[0] == 'logo.png'
assert result[1] == 'banner.jpg'
assert result[2] == 'icon.svg'
}
// Test empty content
fn test_extract_image_links_empty() {
content := ''
result := extract_image_links(content, false) or { panic(err) }
assert result.len == 0
}
// Test content with no images
fn test_extract_image_links_no_images() {
content := 'This is just plain text with no images'
result := extract_image_links(content, false) or { panic(err) }
assert result.len == 0
}
// Test content with regular links (not images)
fn test_extract_image_links_regular_links() {
content := '[regular link](page.md) and [another](doc.html)'
result := extract_image_links(content, false) or { panic(err) }
assert result.len == 0
}
// Test HTTP URLs with exclude_http = true
fn test_extract_image_links_exclude_http() {
content := '![local](local.png) ![remote](http://example.com/image.jpg) ![https](https://example.com/logo.png)'
result := extract_image_links(content, true) or { panic(err) }
assert result.len == 1
assert result[0] == 'local.png'
}
// Test HTTP URLs with exclude_http = false
fn test_extract_image_links_include_http() {
content := '![local](local.png) ![remote](http://example.com/image.jpg) ![https](https://example.com/logo.png)'
result := extract_image_links(content, false) or { panic(err) }
assert result.len == 3
assert result[0] == 'local.png'
assert result[1] == 'image.jpg'
assert result[2] == 'logo.png'
}
// Test image paths with directories
fn test_extract_image_links_with_paths() {
content := '![img1](images/logo.png) ![img2](../assets/banner.jpg) ![img3](./icons/icon.svg)'
result := extract_image_links(content, false) or { panic(err) }
assert result.len == 3
assert result[0] == 'logo.png'
assert result[1] == 'banner.jpg'
assert result[2] == 'icon.svg'
}
// Test various image formats
fn test_extract_image_links_formats() {
content := '![png](img.png) ![jpg](img.jpg) ![jpeg](img.jpeg) ![gif](img.gif) ![svg](img.svg) ![webp](img.webp) ![bmp](img.bmp)'
result := extract_image_links(content, false) or { panic(err) }
assert result.len == 7
assert 'img.png' in result
assert 'img.jpg' in result
assert 'img.jpeg' in result
assert 'img.gif' in result
assert 'img.svg' in result
assert 'img.webp' in result
assert 'img.bmp' in result
}
// Test malformed markdown - missing closing bracket
fn test_extract_image_links_malformed_no_closing_bracket() {
content := '![alt text(image.png)'
result := extract_image_links(content, false) or { panic(err) }
assert result.len == 0
}
// Test malformed markdown - missing opening parenthesis
fn test_extract_image_links_malformed_no_paren() {
content := '![alt text]image.png)'
result := extract_image_links(content, false) or { panic(err) }
assert result.len == 0
}
// Test malformed markdown - missing closing parenthesis
fn test_extract_image_links_malformed_no_closing_paren() {
content := '![alt text](image.png'
result := extract_image_links(content, false) or { panic(err) }
assert result.len == 0
}
// Test empty alt text
fn test_extract_image_links_empty_alt() {
content := '![](image.png)'
result := extract_image_links(content, false) or { panic(err) }
assert result.len == 1
assert result[0] == 'image.png'
}
// Test alt text with special characters
fn test_extract_image_links_special_alt() {
content := '![Logo & Banner - 2024!](logo.png)'
result := extract_image_links(content, false) or { panic(err) }
assert result.len == 1
assert result[0] == 'logo.png'
}
// Test image names with special characters
fn test_extract_image_links_special_names() {
content := '![img1](logo-2024.png) ![img2](banner_v2.jpg) ![img3](icon.final.svg)'
result := extract_image_links(content, false) or { panic(err) }
assert result.len == 3
assert result[0] == 'logo-2024.png'
assert result[1] == 'banner_v2.jpg'
assert result[2] == 'icon.final.svg'
}
// Test mixed content with text, links, and images
fn test_extract_image_links_mixed_content() {
content := '
# Header
Some text with [a link](page.md) and an image ![logo](logo.png).
## Section
More text and ![banner](images/banner.jpg) another image.
[Another link](doc.html)
![icon](icon.svg)
'
result := extract_image_links(content, false) or { panic(err) }
assert result.len == 3
assert result[0] == 'logo.png'
assert result[1] == 'banner.jpg'
assert result[2] == 'icon.svg'
}
// Test consecutive images
fn test_extract_image_links_consecutive() {
content := '![img1](a.png)![img2](b.jpg)![img3](c.svg)'
result := extract_image_links(content, false) or { panic(err) }
assert result.len == 3
assert result[0] == 'a.png'
assert result[1] == 'b.jpg'
assert result[2] == 'c.svg'
}
// Test images with query parameters
fn test_extract_image_links_query_params() {
content := '![img](image.png?size=large&format=webp)'
result := extract_image_links(content, false) or { panic(err) }
assert result.len == 1
// Should extract the full filename including query params
assert result[0].contains('image.png')
}
// Test images with anchors
fn test_extract_image_links_anchors() {
content := '![img](image.png#section)'
result := extract_image_links(content, false) or { panic(err) }
assert result.len == 1
assert result[0].contains('image.png')
}
// Test duplicate images
fn test_extract_image_links_duplicates() {
content := '![img1](logo.png) some text ![img2](logo.png) more text ![img3](logo.png)'
result := extract_image_links(content, false) or { panic(err) }
assert result.len == 3
assert result[0] == 'logo.png'
assert result[1] == 'logo.png'
assert result[2] == 'logo.png'
}
// Test very long content
fn test_extract_image_links_long_content() {
mut content := ''
for i in 0 .. 100 {
content += 'Some text here. '
if i % 10 == 0 {
content += '![img${i}](image${i}.png) '
}
}
result := extract_image_links(content, false) or { panic(err) }
assert result.len == 10
}
// Test image with absolute path
fn test_extract_image_links_absolute_path() {
content := '![img](/absolute/path/to/image.png)'
result := extract_image_links(content, false) or { panic(err) }
assert result.len == 1
assert result[0] == 'image.png'
}
// Test image with Windows-style path
fn test_extract_image_links_windows_path() {
content := '![img](C:\\Users\\images\\logo.png)'
result := extract_image_links(content, false) or { panic(err) }
assert result.len == 1
assert result[0] == 'logo.png'
}
// Test nested brackets in alt text
fn test_extract_image_links_nested_brackets() {
content := '![alt [with] brackets](image.png)'
result := extract_image_links(content, false) or { panic(err) }
// This might not work correctly due to nested brackets
// The function should handle it gracefully
assert result.len >= 0
}
// Test image link at start of string
fn test_extract_image_links_at_start() {
content := '![logo](logo.png) followed by text'
result := extract_image_links(content, false) or { panic(err) }
assert result.len == 1
assert result[0] == 'logo.png'
}
// Test image link at end of string
fn test_extract_image_links_at_end() {
content := 'text followed by ![logo](logo.png)'
result := extract_image_links(content, false) or { panic(err) }
assert result.len == 1
assert result[0] == 'logo.png'
}
// Test only image link
fn test_extract_image_links_only() {
content := '![logo](logo.png)'
result := extract_image_links(content, false) or { panic(err) }
assert result.len == 1
assert result[0] == 'logo.png'
}
// Test whitespace in URL
fn test_extract_image_links_whitespace() {
content := '![img]( image.png )'
result := extract_image_links(content, false) or { panic(err) }
assert result.len == 1
// Should preserve whitespace as-is
assert result[0].contains('image.png')
}
// Test case sensitivity
fn test_extract_image_links_case_sensitivity() {
content := '![img1](Image.PNG) ![img2](LOGO.jpg) ![img3](banner.SVG)'
result := extract_image_links(content, false) or { panic(err) }
assert result.len == 3
assert result[0] == 'Image.PNG'
assert result[1] == 'LOGO.jpg'
assert result[2] == 'banner.SVG'
}

View File

@@ -1,20 +1,19 @@
module docusaurus
import incubaid.herolib.core.pathlib
import incubaid.herolib.web.atlas_client
import incubaid.herolib.web.doctreeclient
import incubaid.herolib.data.atlas.client
import incubaid.herolib.web.site { Page, Section, Site }
import incubaid.herolib.data.markdown.tools as markdowntools
import incubaid.herolib.ui.console
// THIS CODE GENERATES A DOCUSAURUS SITE FROM A DOCUMENT CLIENT AND SITE DEFINITION
// Supports both atlas_client and doctreeclient through the unified IDocClient interface
// Supports both atlas.client and doctreeclient through the unified IDocClient interface
// IDocClient defines the common interface that both atlas_client and doctreeclient implement
// IDocClient defines the common interface that both atlas.client and doctreeclient implement
// This allows the Docusaurus module to work with either client transparently
//
// Note: V interfaces require exact signature matching, so all methods use `mut` receivers
// to match the implementation in both atlas_client and doctreeclient
// to match the implementation in both atlas.client and doctreeclient
pub interface IDocClient {
mut:
// Path methods - get absolute paths to resources
@@ -61,16 +60,7 @@ pub fn (mut docsite DocSite) generate_docs() ! {
docs_path := '${c.path_build.path}/docs'
// Create the appropriate client based on configuration
mut client := if c.use_atlas {
// Use atlas_client for filesystem-based access
if c.atlas_export_dir == '' {
return error('atlas_export_dir is required when use_atlas is true')
}
IDocClient(atlas_client.new(export_dir: c.atlas_export_dir)!)
} else {
// Use doctreeclient for Redis-based access
IDocClient(doctreeclient.new()!)
}
mut client := IDocClient(atlas.client.new(export_dir: c.atlas_export_dir)!)
mut gen := SiteGenerator{
path: pathlib.get_dir(path: docs_path, create: true)!

View File

@@ -19,8 +19,7 @@ pub fn play(mut plbook PlayBook) ! {
reset: param_define.get_default_false('reset')
template_update: param_define.get_default_false('template_update')
install: param_define.get_default_false('install')
use_atlas: param_define.get_default_true('use_atlas')
atlas_export_dir: param_define.get_default('atlas_export_dir', '')!
atlas_dir: param_define.get_default('atlas_dir', '')!
)!
site_name := param_define.get('name') or {