diff --git a/csharp/.changeset/add-pinned-types-decorator.md b/csharp/.changeset/add-pinned-types-decorator.md new file mode 100644 index 0000000..eeae9ce --- /dev/null +++ b/csharp/.changeset/add-pinned-types-decorator.md @@ -0,0 +1,5 @@ +--- +'Foundation.Data.Doublets.Cli': minor +--- + +Added `IPinnedTypes` and `PinnedTypesDecorator`, and composed pinned type support into `NamedTypesDecorator`. diff --git a/csharp/Foundation.Data.Doublets.Cli.Tests/NamedTypesDecoratorTests.cs b/csharp/Foundation.Data.Doublets.Cli.Tests/NamedTypesDecoratorTests.cs index 529b77c..a4ca5a2 100644 --- a/csharp/Foundation.Data.Doublets.Cli.Tests/NamedTypesDecoratorTests.cs +++ b/csharp/Foundation.Data.Doublets.Cli.Tests/NamedTypesDecoratorTests.cs @@ -50,7 +50,7 @@ public void NamedTypesDecorator_ImplementsILinks() RunTestWithLinks(links => { var decorator = new NamedTypesDecorator(links, _tempNamesDbPath); - + Assert.True(decorator is ILinks); }); } @@ -61,8 +61,51 @@ public void NamedTypesDecorator_ImplementsINamedTypes() RunTestWithLinks(links => { var decorator = new NamedTypesDecorator(links, _tempNamesDbPath); - + + Assert.True(decorator is INamedTypes); + }); + } + + [Fact] + public void NamedTypesDecorator_ImplementsIPinnedTypes() + { + RunTestWithLinks(links => + { + var decorator = new NamedTypesDecorator(links, _tempNamesDbPath); + + Assert.True(decorator is IPinnedTypes); + }); + } + + [Fact] + public void NamedTypesDecorator_UsesProvidedPinnedTypesDecorator() + { + RunTestWithLinks(links => + { + var pinnedTypesDecorator = new PinnedTypesDecorator(links); + var decorator = new NamedTypesDecorator(pinnedTypesDecorator, _tempNamesDbPath); + + Assert.Same(pinnedTypesDecorator, decorator.PinnedTypesDecorator); + Assert.True(decorator is ILinks); Assert.True(decorator is INamedTypes); + Assert.True(decorator is IPinnedTypes); + }); + } + + [Fact] + public void NamedTypesDecorator_CanEnumeratePinnedTypes() + { + RunTestWithLinks(links => + { + var decorator = new NamedTypesDecorator(links, _tempNamesDbPath); + + var types = decorator.Take(3).ToArray(); + var (type1, type2, type3) = decorator; + + Assert.Equal(new uint[] { 1, 2, 3 }, types); + Assert.Equal(1u, type1); + Assert.Equal(2u, type2); + Assert.Equal(3u, type3); }); } @@ -72,19 +115,19 @@ public void NamedTypesDecorator_CanSetAndGetNames() RunTestWithLinks(links => { var decorator = new NamedTypesDecorator(links, _tempNamesDbPath); - + var link1 = decorator.GetOrCreate(10u, 20u); var link2 = decorator.GetOrCreate(30u, 40u); - + var nameLink1 = decorator.SetName(link1, "TestLink1"); var nameLink2 = decorator.SetName(link2, "TestLink2"); - + Assert.NotEqual(links.Constants.Null, nameLink1); Assert.NotEqual(links.Constants.Null, nameLink2); - + var retrievedName1 = decorator.GetName(link1); var retrievedName2 = decorator.GetName(link2); - + Assert.Equal("TestLink1", retrievedName1); Assert.Equal("TestLink2", retrievedName2); }); @@ -96,12 +139,12 @@ public void NamedTypesDecorator_CanGetLinkByName() RunTestWithLinks(links => { var decorator = new NamedTypesDecorator(links, _tempNamesDbPath); - + var link = decorator.GetOrCreate(50u, 60u); decorator.SetName(link, "UniqueTestName"); - + var retrievedLink = decorator.GetByName("UniqueTestName"); - + Assert.Equal(link, retrievedLink); }); } @@ -112,18 +155,18 @@ public void NamedTypesDecorator_CanRemoveNames() RunTestWithLinks(links => { var decorator = new NamedTypesDecorator(links, _tempNamesDbPath); - + var link = decorator.GetOrCreate(70u, 80u); decorator.SetName(link, "TemporaryName"); - + var nameBeforeRemoval = decorator.GetName(link); Assert.Equal("TemporaryName", nameBeforeRemoval); - + decorator.RemoveName(link); - + var nameAfterRemoval = decorator.GetName(link); Assert.Null(nameAfterRemoval); - + var linkByName = decorator.GetByName("TemporaryName"); Assert.Equal(links.Constants.Null, linkByName); }); @@ -135,21 +178,21 @@ public void NamedTypesDecorator_CanOverwriteNames() RunTestWithLinks(links => { var decorator = new NamedTypesDecorator(links, _tempNamesDbPath); - + var link = decorator.GetOrCreate(90u, 100u); decorator.SetName(link, "FirstName"); - + var firstRetrievedName = decorator.GetName(link); Assert.Equal("FirstName", firstRetrievedName); - + decorator.SetName(link, "SecondName"); - + var secondRetrievedName = decorator.GetName(link); Assert.Equal("SecondName", secondRetrievedName); - + var linkByFirstName = decorator.GetByName("FirstName"); Assert.Equal(links.Constants.Null, linkByFirstName); - + var linkBySecondName = decorator.GetByName("SecondName"); Assert.Equal(link, linkBySecondName); }); @@ -180,15 +223,15 @@ public void NamedTypesDecorator_DeleteRemovesAssociatedNames() RunTestWithLinks(links => { var decorator = new NamedTypesDecorator(links, _tempNamesDbPath); - + var link = decorator.GetOrCreate(110u, 120u); decorator.SetName(link, "LinkToDelete"); - + var nameBeforeDeletion = decorator.GetName(link); Assert.Equal("LinkToDelete", nameBeforeDeletion); - + decorator.Delete(new uint[] { link }, null); - + var linkByName = decorator.GetByName("LinkToDelete"); Assert.Equal(links.Constants.Null, linkByName); }); @@ -223,10 +266,10 @@ public void NamedTypesDecorator_HandlesNonexistentNames() RunTestWithLinks(links => { var decorator = new NamedTypesDecorator(links, _tempNamesDbPath); - + var linkByNonexistentName = decorator.GetByName("NonexistentName"); Assert.Equal(links.Constants.Null, linkByNonexistentName); - + var nameOfNonexistentLink = decorator.GetName(999999u); Assert.Null(nameOfNonexistentLink); }); diff --git a/csharp/Foundation.Data.Doublets.Cli.Tests/PinnedTypesDecoratorTests.cs b/csharp/Foundation.Data.Doublets.Cli.Tests/PinnedTypesDecoratorTests.cs new file mode 100644 index 0000000..5b6df32 --- /dev/null +++ b/csharp/Foundation.Data.Doublets.Cli.Tests/PinnedTypesDecoratorTests.cs @@ -0,0 +1,115 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using Platform.Data; +using Platform.Data.Doublets; +using Platform.Data.Doublets.Memory.United.Generic; +using Xunit; + +namespace Foundation.Data.Doublets.Cli.Tests +{ + public class PinnedTypesDecoratorTests + { + [Fact] + public void Should_Implement_Both_ILinks_And_IPinnedTypes() + { + // Arrange + var tempDbFile = Path.GetTempFileName(); + try + { + using var links = new UnitedMemoryLinks(tempDbFile); + + // Act + var decorator = new PinnedTypesDecorator(links); + + // Assert - Should implement both interfaces + Assert.IsAssignableFrom>(decorator); + Assert.IsAssignableFrom>(decorator); + } + finally + { + File.Delete(tempDbFile); + } + } + + [Fact] + public void Should_Enumerate_PinnedTypes() + { + // Arrange + var tempDbFile = Path.GetTempFileName(); + try + { + using var links = new UnitedMemoryLinks(tempDbFile); + var decorator = new PinnedTypesDecorator(links); + var numberOfTypes = 3; + + // Act + var result = new List(); + foreach (var type in decorator.Take(numberOfTypes)) + { + result.Add(type); + } + + // Assert + Assert.Equal(numberOfTypes, result.Count); + Assert.Equal(new ulong[] { 1, 2, 3 }, result); + } + finally + { + File.Delete(tempDbFile); + } + } + + [Fact] + public void Should_Support_Deconstruction() + { + // Arrange + var tempDbFile = Path.GetTempFileName(); + try + { + using var links = new UnitedMemoryLinks(tempDbFile); + var decorator = new PinnedTypesDecorator(links); + var initialSource = 1UL; + + // Pre-create links to ensure they exist + links.GetOrCreate(initialSource, 1UL); + links.GetOrCreate(initialSource, 2UL); + links.GetOrCreate(initialSource, 3UL); + + // Act + var (type1, type2, type3) = decorator; + + // Assert + Assert.Equal(1UL, type1); + Assert.Equal(2UL, type2); + Assert.Equal(3UL, type3); + } + finally + { + File.Delete(tempDbFile); + } + } + + [Fact] + public void Should_Work_As_ILinks_Decorator() + { + // Arrange + var tempDbFile = Path.GetTempFileName(); + try + { + using var baseLinks = new UnitedMemoryLinks(tempDbFile); + var decorator = new PinnedTypesDecorator(baseLinks); + + // Act & Assert - Test that it still works as a decorator and properly implements both interfaces + Assert.NotNull(decorator); + Assert.IsAssignableFrom>(decorator); + Assert.IsAssignableFrom>(decorator); + } + finally + { + File.Delete(tempDbFile); + } + } + } +} diff --git a/csharp/Foundation.Data.Doublets.Cli/NamedTypesDecorator.cs b/csharp/Foundation.Data.Doublets.Cli/NamedTypesDecorator.cs index c837f1d..fa91e85 100644 --- a/csharp/Foundation.Data.Doublets.Cli/NamedTypesDecorator.cs +++ b/csharp/Foundation.Data.Doublets.Cli/NamedTypesDecorator.cs @@ -1,3 +1,6 @@ +using System; +using System.Collections; +using System.Collections.Generic; using System.Numerics; using Platform.Delegates; using Platform.Memory; @@ -6,13 +9,10 @@ using Platform.Data.Doublets.Decorators; using Platform.Data.Doublets.Memory; using Platform.Data.Doublets.Memory.United.Generic; -using System; -using System.Linq; -using System.Collections.Generic; namespace Foundation.Data.Doublets.Cli { - public class NamedTypesDecorator : LinksDecoratorBase, INamedTypes + public class NamedTypesDecorator : LinksDecoratorBase, INamedTypes, IPinnedTypes where TLinkAddress : struct, IUnsignedNumber, IComparisonOperators, @@ -21,6 +21,7 @@ public class NamedTypesDecorator : LinksDecoratorBase { private readonly bool _tracingEnabled; + public readonly PinnedTypesDecorator PinnedTypesDecorator; public readonly NamedLinks NamedLinks; public readonly string NamedLinksDatabaseFileName; @@ -38,9 +39,15 @@ public static string MakeNamesDatabaseFilename(string databaseFilename) return namesDatabaseFilename; } - public NamedTypesDecorator(ILinks links, string namesDatabaseFilename, bool tracingEnabled = false) : base(links) + public NamedTypesDecorator(ILinks links, string namesDatabaseFilename, bool tracingEnabled = false) + : this(new PinnedTypesDecorator(links), namesDatabaseFilename, tracingEnabled) + { + } + + public NamedTypesDecorator(PinnedTypesDecorator pinnedTypesDecorator, string namesDatabaseFilename, bool tracingEnabled = false) : base(pinnedTypesDecorator) { _tracingEnabled = tracingEnabled; + PinnedTypesDecorator = pinnedTypesDecorator; if (_tracingEnabled) Console.WriteLine($"[Trace] Constructing NamedTypesDecorator with names DB: {namesDatabaseFilename}"); var namesConstants = new LinksConstants(enableExternalReferencesSupport: true); var namesMemory = new FileMappedResizableDirectMemory(namesDatabaseFilename, UnitedMemoryLinks.DefaultLinksSizeStep); @@ -55,6 +62,21 @@ public NamedTypesDecorator(string databaseFilename, bool tracingEnabled = false) { } + public IEnumerator GetEnumerator() + { + return PinnedTypesDecorator.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + public void Deconstruct(out TLinkAddress type1, out TLinkAddress type2, out TLinkAddress type3) + { + PinnedTypesDecorator.Deconstruct(out type1, out type2, out type3); + } + public string? GetName(TLinkAddress link) { if (_tracingEnabled) Console.WriteLine($"[Trace] GetName called for link: {link}"); diff --git a/csharp/Foundation.Data.Doublets.Cli/PinnedTypes.cs b/csharp/Foundation.Data.Doublets.Cli/PinnedTypes.cs index 591de18..027afac 100644 --- a/csharp/Foundation.Data.Doublets.Cli/PinnedTypes.cs +++ b/csharp/Foundation.Data.Doublets.Cli/PinnedTypes.cs @@ -6,7 +6,13 @@ namespace Foundation.Data.Doublets.Cli { - public class PinnedTypes : IEnumerable + public interface IPinnedTypes : IEnumerable + where TLinkAddress : struct, System.Numerics.IUnsignedNumber + { + void Deconstruct(out TLinkAddress type1, out TLinkAddress type2, out TLinkAddress type3); + } + + public class PinnedTypes : IPinnedTypes where TLinkAddress : struct, System.Numerics.IUnsignedNumber { private readonly ILinks _links; @@ -83,5 +89,19 @@ public void Dispose() // No resources to dispose } } + + public void Deconstruct(out TLinkAddress type1, out TLinkAddress type2, out TLinkAddress type3) + { + using var enumerator = GetEnumerator(); + + enumerator.MoveNext(); + type1 = enumerator.Current; + + enumerator.MoveNext(); + type2 = enumerator.Current; + + enumerator.MoveNext(); + type3 = enumerator.Current; + } } -} \ No newline at end of file +} diff --git a/csharp/Foundation.Data.Doublets.Cli/PinnedTypesDecorator.cs b/csharp/Foundation.Data.Doublets.Cli/PinnedTypesDecorator.cs new file mode 100644 index 0000000..e279e52 --- /dev/null +++ b/csharp/Foundation.Data.Doublets.Cli/PinnedTypesDecorator.cs @@ -0,0 +1,40 @@ +using System.Collections; +using System.Collections.Generic; +using System.Numerics; +using Platform.Data; +using Platform.Data.Doublets; +using Platform.Data.Doublets.Decorators; + +namespace Foundation.Data.Doublets.Cli +{ + public class PinnedTypesDecorator : LinksDecoratorBase, IPinnedTypes + where TLinkAddress : struct, + IUnsignedNumber, + IComparisonOperators, + IShiftOperators, + IBitwiseOperators, + IMinMaxValue + { + private readonly IPinnedTypes _pinnedTypes; + + public PinnedTypesDecorator(ILinks links) : base(links) + { + _pinnedTypes = new PinnedTypes(links); + } + + public IEnumerator GetEnumerator() + { + return _pinnedTypes.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + public void Deconstruct(out TLinkAddress type1, out TLinkAddress type2, out TLinkAddress type3) + { + _pinnedTypes.Deconstruct(out type1, out type2, out type3); + } + } +} diff --git a/rust/changelog.d/20260430_073500_pinned_types_decorator.md b/rust/changelog.d/20260430_073500_pinned_types_decorator.md new file mode 100644 index 0000000..7435b30 --- /dev/null +++ b/rust/changelog.d/20260430_073500_pinned_types_decorator.md @@ -0,0 +1,5 @@ +--- +bump: minor +--- + +Added Rust pinned type decorator parity and composed pinned type access into the named types decorator. diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 062088d..5591ef5 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -41,7 +41,7 @@ pub use lino_link::LinoLink; pub use named_links::NamedLinks; pub use named_types::{NamedTypes, NamedTypesDecorator}; pub use parser::Parser; -pub use pinned_types::PinnedTypes; +pub use pinned_types::{PinnedTypes, PinnedTypesAccess, PinnedTypesDecorator}; pub use query_options::QueryOptions; pub use query_processor::QueryProcessor; pub use unicode_string_storage::UnicodeStringStorage; diff --git a/rust/src/named_types.rs b/rust/src/named_types.rs index 57a8704..a957e19 100644 --- a/rust/src/named_types.rs +++ b/rust/src/named_types.rs @@ -1,8 +1,8 @@ //! Named type decorator for Rust link storage. //! -//! This mirrors the C# `NamedTypesDecorator` role: link operations are -//! delegated to the wrapped `LinkStorage`, while names are stored as external -//! references in a separate links database. +//! This mirrors the C# `NamedTypesDecorator` role: link operations and +//! pinned type access are delegated through `PinnedTypesDecorator`, while names +//! are stored as external references in a separate links database. use std::path::{Path, PathBuf}; @@ -11,6 +11,7 @@ use anyhow::Result; use crate::link::Link; use crate::link_storage::LinkStorage; use crate::named_links::NamedLinks; +use crate::pinned_types::{PinnedTypesAccess, PinnedTypesDecorator}; pub trait NamedTypes { fn get_name(&mut self, link: u32) -> Result>; @@ -20,7 +21,7 @@ pub trait NamedTypes { } pub struct NamedTypesDecorator { - links: LinkStorage, + pinned_types_decorator: PinnedTypesDecorator, names_links: LinkStorage, trace: bool, } @@ -59,6 +60,13 @@ impl NamedTypesDecorator { Self::from_link_storages_with_trace(links, names_links, false) } + pub fn from_pinned_types_decorator( + pinned_types_decorator: PinnedTypesDecorator, + names_links: LinkStorage, + ) -> Self { + Self::from_decorators_with_trace(pinned_types_decorator, names_links, false) + } + pub fn make_names_database_filename

(database_filename: P) -> PathBuf where P: AsRef, @@ -77,11 +85,19 @@ impl NamedTypesDecorator { } pub fn links(&self) -> &LinkStorage { - &self.links + self.pinned_types_decorator.links() } pub fn links_mut(&mut self) -> &mut LinkStorage { - &mut self.links + self.pinned_types_decorator.links_mut() + } + + pub fn pinned_types_decorator(&self) -> &PinnedTypesDecorator { + &self.pinned_types_decorator + } + + pub fn pinned_types_decorator_mut(&mut self) -> &mut PinnedTypesDecorator { + &mut self.pinned_types_decorator } pub fn names_links(&self) -> &LinkStorage { @@ -93,43 +109,46 @@ impl NamedTypesDecorator { } pub fn into_link_storages(self) -> (LinkStorage, LinkStorage) { - (self.links, self.names_links) + ( + self.pinned_types_decorator.into_link_storage(), + self.names_links, + ) } pub fn save(&self) -> Result<()> { - self.links.save()?; + self.pinned_types_decorator.save()?; self.names_links.save()?; Ok(()) } pub fn create(&mut self, source: u32, target: u32) -> u32 { - self.links.create(source, target) + self.pinned_types_decorator.create(source, target) } pub fn ensure_created(&mut self, id: u32) -> u32 { - self.links.ensure_created(id) + self.pinned_types_decorator.ensure_created(id) } pub fn get(&self, id: u32) -> Option<&Link> { - self.links.get(id) + self.pinned_types_decorator.get(id) } pub fn exists(&self, id: u32) -> bool { - self.links.exists(id) + self.pinned_types_decorator.exists(id) } pub fn update(&mut self, id: u32, source: u32, target: u32) -> Result { - self.links.update(id, source, target) + self.pinned_types_decorator.update(id, source, target) } pub fn delete(&mut self, id: u32) -> Result { - let deleted = self.links.delete(id)?; + let deleted = self.pinned_types_decorator.delete(id)?; self.remove_name(id)?; Ok(deleted) } pub fn all(&self) -> Vec<&Link> { - self.links.all() + self.pinned_types_decorator.all() } pub fn query( @@ -138,24 +157,36 @@ impl NamedTypesDecorator { source: Option, target: Option, ) -> Vec<&Link> { - self.links.query(index, source, target) + self.pinned_types_decorator.query(index, source, target) } pub fn search(&self, source: u32, target: u32) -> Option { - self.links.search(source, target) + self.pinned_types_decorator.search(source, target) } pub fn get_or_create(&mut self, source: u32, target: u32) -> u32 { - self.links.get_or_create(source, target) + self.pinned_types_decorator.get_or_create(source, target) } fn from_link_storages_with_trace( links: LinkStorage, names_links: LinkStorage, trace: bool, + ) -> Self { + Self::from_decorators_with_trace( + PinnedTypesDecorator::from_link_storage(links), + names_links, + trace, + ) + } + + fn from_decorators_with_trace( + pinned_types_decorator: PinnedTypesDecorator, + names_links: LinkStorage, + trace: bool, ) -> Self { Self { - links, + pinned_types_decorator, names_links, trace, } @@ -206,6 +237,12 @@ impl NamedTypes for NamedTypesDecorator { } } +impl PinnedTypesAccess for NamedTypesDecorator { + fn pinned_types(&mut self, count: usize) -> Result> { + self.pinned_types_decorator.pinned_types(count) + } +} + fn path_to_string(path: &Path) -> String { path.to_string_lossy().into_owned() } diff --git a/rust/src/pinned_types.rs b/rust/src/pinned_types.rs index aefc125..12dc6f6 100644 --- a/rust/src/pinned_types.rs +++ b/rust/src/pinned_types.rs @@ -1,9 +1,20 @@ //! Pinned type allocation compatible with the C# `PinnedTypes` helper. use anyhow::{bail, Result}; +use std::path::Path; +use crate::link::Link; use crate::link_storage::LinkStorage; +pub trait PinnedTypesAccess { + fn pinned_types(&mut self, count: usize) -> Result>; + + fn deconstruct_pinned_types(&mut self) -> Result<(u32, u32, u32)> { + let pinned_types = self.pinned_types(3)?; + Ok((pinned_types[0], pinned_types[1], pinned_types[2])) + } +} + /// Creates or validates the reserved type links at deterministic addresses. pub struct PinnedTypes<'a> { links: &'a mut LinkStorage, @@ -45,4 +56,100 @@ impl<'a> PinnedTypes<'a> { self.current += 1; Ok(address) } + + pub fn take_types(&mut self, count: usize) -> Result> { + (0..count).map(|_| self.next_type()).collect() + } + + pub fn deconstruct(&mut self) -> Result<(u32, u32, u32)> { + let pinned_types = self.take_types(3)?; + Ok((pinned_types[0], pinned_types[1], pinned_types[2])) + } +} + +/// Link storage decorator that exposes pinned type allocation. +pub struct PinnedTypesDecorator { + links: LinkStorage, +} + +impl PinnedTypesDecorator { + pub fn new

(database_filename: P, trace: bool) -> Result + where + P: AsRef, + { + let database_path = database_filename.as_ref().to_string_lossy().into_owned(); + let links = LinkStorage::new(&database_path, trace)?; + Ok(Self::from_link_storage(links)) + } + + pub fn from_link_storage(links: LinkStorage) -> Self { + Self { links } + } + + pub fn links(&self) -> &LinkStorage { + &self.links + } + + pub fn links_mut(&mut self) -> &mut LinkStorage { + &mut self.links + } + + pub fn into_link_storage(self) -> LinkStorage { + self.links + } + + pub fn save(&self) -> Result<()> { + self.links.save() + } + + pub fn create(&mut self, source: u32, target: u32) -> u32 { + self.links.create(source, target) + } + + pub fn ensure_created(&mut self, id: u32) -> u32 { + self.links.ensure_created(id) + } + + pub fn get(&self, id: u32) -> Option<&Link> { + self.links.get(id) + } + + pub fn exists(&self, id: u32) -> bool { + self.links.exists(id) + } + + pub fn update(&mut self, id: u32, source: u32, target: u32) -> Result { + self.links.update(id, source, target) + } + + pub fn delete(&mut self, id: u32) -> Result { + self.links.delete(id) + } + + pub fn all(&self) -> Vec<&Link> { + self.links.all() + } + + pub fn query( + &self, + index: Option, + source: Option, + target: Option, + ) -> Vec<&Link> { + self.links.query(index, source, target) + } + + pub fn search(&self, source: u32, target: u32) -> Option { + self.links.search(source, target) + } + + pub fn get_or_create(&mut self, source: u32, target: u32) -> u32 { + self.links.get_or_create(source, target) + } +} + +impl PinnedTypesAccess for PinnedTypesDecorator { + fn pinned_types(&mut self, count: usize) -> Result> { + PinnedTypes::new(&mut self.links).take_types(count) + } } diff --git a/rust/tests/named_types_decorator_tests.rs b/rust/tests/named_types_decorator_tests.rs index 0ad12bc..49a976d 100644 --- a/rust/tests/named_types_decorator_tests.rs +++ b/rust/tests/named_types_decorator_tests.rs @@ -1,5 +1,7 @@ use anyhow::Result; -use link_cli::{LinkStorage, NamedTypes, NamedTypesDecorator}; +use link_cli::{ + LinkStorage, NamedTypes, NamedTypesDecorator, PinnedTypesAccess, PinnedTypesDecorator, +}; use tempfile::NamedTempFile; #[test] @@ -22,6 +24,32 @@ fn decorator_exposes_link_storage_operations_and_named_types() -> Result<()> { Ok(()) } +#[test] +fn decorator_includes_pinned_types_decorator() -> Result<()> { + let db_file = NamedTempFile::new()?; + let names_file = NamedTempFile::new()?; + let db_path = db_file.path().to_str().unwrap(); + let names_path = names_file.path().to_str().unwrap(); + + let links = LinkStorage::new(db_path, false)?; + let names_links = LinkStorage::new(names_path, false)?; + let pinned_types_decorator = PinnedTypesDecorator::from_link_storage(links); + let mut decorator = + NamedTypesDecorator::from_pinned_types_decorator(pinned_types_decorator, names_links); + + assert_eq!(vec![1, 2, 3], decorator.pinned_types(3)?); + assert!(decorator.pinned_types_decorator().exists(1)); + assert!(decorator.pinned_types_decorator().exists(2)); + assert!(decorator.pinned_types_decorator().exists(3)); + + let link = decorator.get_or_create(10, 20); + decorator.set_name(link, "PinnedNamedLink")?; + + assert_eq!(Some(link), decorator.get_by_name("PinnedNamedLink")?); + + Ok(()) +} + #[test] fn delete_removes_associated_name_from_names_database() -> Result<()> { let db_file = NamedTempFile::new()?; diff --git a/rust/tests/pinned_types_decorator_tests.rs b/rust/tests/pinned_types_decorator_tests.rs new file mode 100644 index 0000000..60e8f05 --- /dev/null +++ b/rust/tests/pinned_types_decorator_tests.rs @@ -0,0 +1,58 @@ +use anyhow::Result; +use link_cli::{LinkStorage, PinnedTypesAccess, PinnedTypesDecorator}; +use tempfile::NamedTempFile; + +#[test] +fn decorator_exposes_link_storage_operations_and_pinned_types() -> Result<()> { + let db_file = NamedTempFile::new()?; + let db_path = db_file.path().to_str().unwrap(); + let links = LinkStorage::new(db_path, false)?; + let mut decorator = PinnedTypesDecorator::from_link_storage(links); + + assert_eq!(vec![1, 2, 3], decorator.pinned_types(3)?); + assert!(decorator.exists(1)); + assert!(decorator.exists(2)); + assert!(decorator.exists(3)); + + let link = decorator.get_or_create(10, 20); + + assert!(decorator.links().exists(link)); + assert_eq!(Some(link), decorator.search(10, 20)); + + Ok(()) +} + +#[test] +fn decorator_supports_triplet_deconstruction_parity() -> Result<()> { + let db_file = NamedTempFile::new()?; + let db_path = db_file.path().to_str().unwrap(); + let links = LinkStorage::new(db_path, false)?; + let mut decorator = PinnedTypesDecorator::from_link_storage(links); + + let (type_type, unicode_symbol_type, unicode_sequence_type) = + decorator.deconstruct_pinned_types()?; + + assert_eq!( + (1, 2, 3), + (type_type, unicode_symbol_type, unicode_sequence_type) + ); + + Ok(()) +} + +#[test] +fn decorator_rejects_unexpected_link_shape_at_reserved_address() -> Result<()> { + let db_file = NamedTempFile::new()?; + let db_path = db_file.path().to_str().unwrap(); + let links = LinkStorage::new(db_path, false)?; + let mut decorator = PinnedTypesDecorator::from_link_storage(links); + + decorator.create(1, 0); + let error = decorator.pinned_types(1).unwrap_err(); + + assert!(error + .to_string() + .contains("Unexpected link found at address 1")); + + Ok(()) +}