Translate appearance and outfit parity tests

This commit is contained in:
2026-08-08 20:29:15 +02:00
parent ffe559db61
commit a78f9fba76
21 changed files with 4111 additions and 1902 deletions

View File

@@ -57,6 +57,8 @@ pub enum Error {
IndexOutOfRange,
/// An operation observed a requested cancellation.
Cancelled,
/// An HTTP request completed with an unsuccessful response.
HttpRequest,
}
impl Error {
@@ -69,7 +71,8 @@ impl Error {
| Self::Argument
| Self::InvalidOperation
| Self::IndexOutOfRange
| Self::Cancelled => None,
| Self::Cancelled
| Self::HttpRequest => None,
}
}
}
@@ -91,6 +94,7 @@ impl fmt::Display for Error {
}
Self::IndexOutOfRange => formatter.write_str("index was out of range"),
Self::Cancelled => formatter.write_str("operation was cancelled"),
Self::HttpRequest => formatter.write_str("HTTP request failed"),
}
}
}

View File

@@ -0,0 +1,99 @@
// Exact Rust translations of LibreMetaverse.Tests/BakerTests.cs.
// Source SHA-256: 7727140f0fbf2c77ed7a6f3f468798d8f7ba96c5d9889246a2bccd2d4bd8def2
use super::*;
use libremetaverse_imaging::{ManagedImage, ManagedImageImageChannels};
use libremetaverse_types::Color4;
fn gray_alpha_image(value: u8) -> ManagedImage {
let channels = ManagedImageImageChannels(
ManagedImageImageChannels::GRAY.0 | ManagedImageImageChannels::ALPHA.0,
);
let mut image = ManagedImage::new(4, 4, channels).unwrap();
image.red.fill(value);
image.alpha.fill(255);
image
}
fn texture_data(
image: ManagedImage,
color: Color4,
index: AvatarTextureIndex,
) -> AppearanceManagerTextureData {
let mut data = AppearanceManagerTextureData::new().unwrap();
data.texture = Some(assets::AssetTexture::new_with_managed_image(image).unwrap());
data.color = color;
data.texture_index = index;
data
}
// parity-case: LibreMetaverse.Tests/BakerTests.cs::BakerTests.Bake_GrayscaleLayerTexture_DoesNotThrow::test c3fc1d2c16b5f8d69678a6cf05a3a275e0b657f3a6e7f6788f350f2a107200b3 translated
#[test]
fn bake_grayscale_layer_texture_does_not_throw() {
let color = Color4::new_with_single_single_single_single(0.5, 0.25, 0.75, 1.0).unwrap();
let data = texture_data(gray_alpha_image(128), color, AvatarTextureIndex::UpperShirt);
let mut baker = imaging::Baker::new(BakeType::UpperBody).unwrap();
baker.add_texture(data).unwrap();
baker.bake().unwrap();
assert!(baker.baked_texture().is_some());
}
// parity-case: LibreMetaverse.Tests/BakerTests.cs::BakerTests.Bake_GrayscaleHairTextureNoSkin_DoesNotThrow::test e48b737d9138e63cda98b5e80bf561a80d1dab5de78901ac9e1bb320ca281eb6 translated
#[test]
fn bake_grayscale_hair_texture_no_skin_does_not_throw() {
let data = texture_data(
gray_alpha_image(200),
Color4::white(),
AvatarTextureIndex::Hair,
);
let mut baker = imaging::Baker::new(BakeType::Head).unwrap();
baker.add_texture(data).unwrap();
baker.bake().unwrap();
assert!(baker.baked_texture().is_some());
}
// parity-case: LibreMetaverse.Tests/BakerTests.cs::BakerTests.LoadResourceLayer_HeadColorTga_LoadsFromCharacterDirectory::test 5a9b80005a51f04a227ca392218725d50704d038783e04fc49bb955997c22e76 translated
#[test]
fn load_resource_layer_head_color_tga_loads_from_character_directory() {
assert!(
imaging::Baker::load_resource_layer("head_color.tga".into())
.unwrap()
.is_some()
);
}
// parity-case: LibreMetaverse.Tests/BakerTests.cs::BakerTests.LoadResourceLayer_CachedResourceNotCorruptedByCallerMutation::test 18b458bbd7bb13d2b68711f7d3495193f1b4edac9c1f776930ad2c172f97cd49 translated
#[test]
fn load_resource_layer_cached_resource_not_corrupted_by_caller_mutation() {
imaging::Baker::load_resource_layer("head_color.tga".into()).unwrap();
let mut second = imaging::Baker::load_resource_layer("head_color.tga".into())
.unwrap()
.unwrap();
let original_width = second.width;
let original_height = second.height;
second
.resize_nearest_neighbor(original_width / 2, original_height / 2)
.unwrap();
let third = imaging::Baker::load_resource_layer("head_color.tga".into())
.unwrap()
.unwrap();
assert_eq!(third.width, original_width);
assert_eq!(third.height, original_height);
}
// parity-case: LibreMetaverse.Tests/BakerTests.cs::BakerTests.ResizeToBakeDimensions_SourceSmallerInOneAxisOnly_ScalesRatherThanTiles::test ce5bd346b48cb19dbc0248403eafefb07309d4e05e675e9a02db5a46d7cd65ff translated
#[test]
fn resize_to_bake_dimensions_source_smaller_in_one_axis_only_scales_rather_than_tiles() {
let mut source = ManagedImage::new(2, 8, ManagedImageImageChannels::COLOR).unwrap();
for y in 0..8 {
for x in 0..2 {
source.red[y * 2 + x] = u8::try_from(y * 10).unwrap();
}
}
let result = imaging::Baker::resize_to_bake_dimensions(source, 4, 4)
.unwrap()
.unwrap();
assert_eq!(result.width, 4);
assert_eq!(result.height, 4);
assert!(result.red[3 * 4] > 50);
}

View File

@@ -0,0 +1,459 @@
// Exact Rust translations of LibreMetaverse.Tests/BatchLinkTests.cs.
// Source SHA-256: 092ccb2686769d27f6a8072114654e91308f88edd94223a77af92f56426ee1ee
use super::*;
use libremetaverse_structured_data::{OSD, OSDParser};
use libremetaverse_types::compat::{HttpMessageHandler, HttpRequest, HttpResponse, Uri};
use libremetaverse_types::{AssetType, InventoryType, UUID, WearableType};
use std::collections::{BTreeMap, HashMap, VecDeque};
use std::fmt::Write as _;
use std::sync::{Arc, Mutex};
const CAP: &str = "http://fake-ais3.test/ais3";
const LIBRARY_CAP: &str = "http://fake-lib.test/lib/";
const SEED: &str = "http://fake-seed.test/caps";
fn block_on<F: std::future::Future>(future: F) -> F::Output {
use std::task::{Context, Poll, Wake, Waker};
struct ThreadWake(std::thread::Thread);
impl Wake for ThreadWake {
fn wake(self: Arc<Self>) {
self.0.unpark();
}
}
let waker = Waker::from(Arc::new(ThreadWake(std::thread::current())));
let mut context = Context::from_waker(&waker);
let mut future = std::pin::pin!(future);
loop {
match future.as_mut().poll(&mut context) {
Poll::Ready(output) => return output,
Poll::Pending => std::thread::park(),
}
}
}
#[derive(Clone)]
struct RecordingHttp(Arc<Mutex<Vec<HttpRequest>>>);
impl RecordingHttp {
fn requests(&self) -> Vec<HttpRequest> {
self.0.lock().unwrap().clone()
}
}
fn client_with_cap(responses: Vec<(String, u16, String)>) -> (GridClient, RecordingHttp) {
let requests = Arc::new(Mutex::new(Vec::new()));
let recorded = Arc::clone(&requests);
let mut queues = HashMap::<String, VecDeque<HttpResponse>>::new();
for (uri, status, body) in responses {
queues.entry(uri).or_default().push_back(HttpResponse {
status_code: status,
headers: BTreeMap::new(),
content_type: Some("application/llsd+xml".into()),
body: body.into_bytes(),
});
}
let queues = Mutex::new(queues);
let seed = format!(
"<llsd><map><key>InventoryAPIv3</key><uri>{CAP}</uri><key>LibraryAPIv3</key><uri>{LIBRARY_CAP}</uri></map></llsd>"
);
let handler = HttpMessageHandler::new(move |request, _| {
let response = if request.uri.0 == SEED {
HttpResponse {
status_code: 200,
headers: BTreeMap::new(),
content_type: Some("application/llsd+xml".into()),
body: seed.as_bytes().to_vec(),
}
} else {
let uri = request.uri.0.clone();
recorded.lock().unwrap().push(request);
queues
.lock()
.unwrap()
.get_mut(&uri)
.and_then(VecDeque::pop_front)
.unwrap_or(HttpResponse {
status_code: 404,
headers: BTreeMap::new(),
content_type: None,
body: Vec::new(),
})
};
async move { response }
});
let mut client = GridClient::new().unwrap();
client.set_http_caps_client(HttpCapsClient::new(handler.clone()).unwrap());
let mut simulator_client = GridClient::new().unwrap();
simulator_client.set_http_caps_client(HttpCapsClient::new(handler).unwrap());
let simulator = Simulator::new(
simulator_client,
"127.0.0.1:0".parse().unwrap(),
0,
None,
None,
)
.unwrap();
simulator
.set_seed_caps(Some(Uri(SEED.into())), Some(true))
.unwrap();
let mut network = client.network();
network.set_current_sim(Some(simulator));
client.set_network(network);
(client, RecordingHttp(requests))
}
fn link_xml(item: UUID, parent: UUID, linked: UUID, name: &str) -> String {
let item = item.to_string();
let parent = parent.to_string();
let linked = linked.to_string();
let agent = UUID::random().unwrap().to_string();
let zero = UUID::zero().to_string();
format!(
"<map><key>item_id</key><uuid>{item}</uuid><key>parent_id</key><uuid>{parent}</uuid><key>linked_id</key><uuid>{linked}</uuid><key>name</key><string>{name}</string><key>desc</key><string></string><key>agent_id</key><uuid>{agent}</uuid><key>inv_type</key><integer>{}</integer><key>type</key><integer>{}</integer><key>created_at</key><integer>0</integer><key>permissions</key><map><key>creator_id</key><uuid>{zero}</uuid><key>last_owner_id</key><uuid>{zero}</uuid><key>base_mask</key><integer>0</integer><key>everyone_mask</key><integer>0</integer><key>group_mask</key><integer>0</integer><key>next_owner_mask</key><integer>0</integer><key>owner_mask</key><integer>0</integer><key>is_owner_group</key><boolean>false</boolean><key>group_id</key><uuid>{zero}</uuid></map><key>sale_info</key><map><key>sale_price</key><integer>0</integer><key>sale_type</key><integer>0</integer></map></map>",
InventoryType::WEARABLE.0,
AssetType::Link as i32,
)
}
fn batch_response(links: &[(UUID, UUID, UUID, &str)]) -> String {
let mut entries = String::new();
for (index, (item, parent, linked, name)) in links.iter().enumerate() {
write!(
entries,
"<key>{index}</key>{}",
link_xml(*item, *parent, *linked, name)
)
.unwrap();
}
format!(
"<llsd><map><key>_embedded</key><map><key>links</key><map>{entries}</map></map></map></llsd>"
)
}
fn wearable(name: &str, wearable_type: WearableType, asset_type: AssetType) -> InventoryWearable {
let mut item = InventoryWearable::new(UUID::random().unwrap()).unwrap();
item.set_wearable_type(wearable_type);
item.base.set_asset_type(asset_type);
item.base.set_inventory_type(InventoryType::WEARABLE);
item.base.base.set_name(name.into());
item
}
fn object(name: &str) -> InventoryObject {
let mut item = InventoryObject::new(UUID::random().unwrap()).unwrap();
item.base.set_asset_type(AssetType::Object);
item.base.set_inventory_type(InventoryType::OBJECT);
item.base.base.set_name(name.into());
item
}
// parity-case: LibreMetaverse.Tests/BatchLinkTests.cs::BatchLinkTests.GetCofLinkDescription_ClothingWearable_ReturnsWearableTypeAndZeroLayer::test 898a14ff28d953a58a01c3a0e5d9d9fcbbe61376a33fdc5caf20268aaac76242 translated
#[test]
fn get_cof_link_description_clothing_wearable_returns_wearable_type_and_zero_layer() {
let cof =
appearance::CurrentOutfitFolder::new(Some(Arc::new(GridClient::new().unwrap()))).unwrap();
let shirt = wearable("Test Shirt", WearableType::Shirt, AssetType::Clothing);
assert_eq!(cof.get_cof_link_description(&shirt).unwrap(), "400");
}
// parity-case: LibreMetaverse.Tests/BatchLinkTests.cs::BatchLinkTests.GetCofLinkDescription_BodyPartShape_ReturnsEmpty::test 4f009136d265da91afb9eafd060faf459f6c510ca0502a33ce9e128d241f9bd1 translated
#[test]
fn get_cof_link_description_body_part_shape_returns_empty() {
let cof =
appearance::CurrentOutfitFolder::new(Some(Arc::new(GridClient::new().unwrap()))).unwrap();
let shape = wearable("Test Shape", WearableType::Shape, AssetType::Bodypart);
assert_eq!(cof.get_cof_link_description(&shape).unwrap(), "");
}
// parity-case: LibreMetaverse.Tests/BatchLinkTests.cs::BatchLinkTests.GetCofLinkDescription_AttachmentObject_ReturnsEmpty::test a838329a7a23081a4022671bc794e7e706c9ab7da51598280a8befbb824d336d translated
#[test]
fn get_cof_link_description_attachment_object_returns_empty() {
let cof =
appearance::CurrentOutfitFolder::new(Some(Arc::new(GridClient::new().unwrap()))).unwrap();
assert_eq!(
cof.get_cof_link_description(&object("Test Attachment"))
.unwrap(),
""
);
}
// parity-case: LibreMetaverse.Tests/BatchLinkTests.cs::BatchLinkTests.GetCofLinkDescription_AllBodyPartTypes_ReturnEmpty::test aa057d55a3b34846567efe382b6e090b5d91935a4ffa6166093b34870ee5da23 translated
#[test]
fn get_cof_link_description_all_body_part_types_return_empty() {
let cof =
appearance::CurrentOutfitFolder::new(Some(Arc::new(GridClient::new().unwrap()))).unwrap();
for wearable_type in [
WearableType::Shape,
WearableType::Skin,
WearableType::Eyes,
WearableType::Hair,
] {
let item = wearable("Test body part", wearable_type, AssetType::Bodypart);
assert_eq!(
cof.get_cof_link_description(&item).unwrap(),
"",
"{wearable_type:?}"
);
}
}
// parity-case: LibreMetaverse.Tests/BatchLinkTests.cs::BatchLinkTests.CreateInventoryLinksAsync_SuccessResponse_ReturnsAllParsedLinks::test 416a1f023a2ae3b883c4c49f0a774c84a2f1a3562abb3ec848f86fe53d929f63 translated
#[test]
fn create_inventory_links_success_response_returns_all_parsed_links() {
let folder = UUID::random().unwrap();
let ids = [
(
UUID::random().unwrap(),
UUID::random().unwrap(),
UUID::random().unwrap(),
"Link1",
),
(
UUID::random().unwrap(),
UUID::random().unwrap(),
UUID::random().unwrap(),
"Link2",
),
];
let uri = format!("{CAP}/category/{}", folder.to_string());
let (client, _) = client_with_cap(vec![(uri, 200, batch_response(&ids))]);
let payload = OSD::Map(HashMap::from([("links".into(), OSD::Array(Vec::new()))]));
let result = block_on(
client
.ais_client()
.create_inventory_links(folder, payload, None),
)
.unwrap();
assert_eq!(result.len(), 2);
let names: Vec<String> = result.iter().map(|item| item.base.name()).collect();
assert!(names.contains(&"Link1".into()));
assert!(names.contains(&"Link2".into()));
}
// parity-case: LibreMetaverse.Tests/BatchLinkTests.cs::BatchLinkTests.CreateInventoryLinksAsync_ServerError_ThrowsHttpRequestException::test 53e3956676e546ab8d69bb63a071028b29151013b6362c39f37516a36cad6641 translated
#[test]
fn create_inventory_links_server_error_throws_http_request_exception() {
let folder = UUID::random().unwrap();
let (client, _) = client_with_cap(vec![(
format!("{CAP}/category/{}", folder.to_string()),
500,
String::new(),
)]);
let Err(error) = block_on(client.ais_client().create_inventory_links(
folder,
OSD::Map(HashMap::new()),
None,
)) else {
panic!("expected HttpRequest error");
};
assert_eq!(error, Error::HttpRequest);
}
// parity-case: LibreMetaverse.Tests/BatchLinkTests.cs::BatchLinkTests.CreateInventoryLinksAsync_NoCapability_ReturnsEmptyList::test d9cd50e620f69e40daab52d175886f3052d3741c5d39ef42549283ff6b2b7b44 translated
#[test]
fn create_inventory_links_no_capability_returns_empty_list() {
let client = GridClient::new().unwrap();
let result = block_on(client.ais_client().create_inventory_links(
UUID::random().unwrap(),
OSD::Map(HashMap::new()),
None,
))
.unwrap();
assert!(result.is_empty());
}
// parity-case: LibreMetaverse.Tests/BatchLinkTests.cs::BatchLinkTests.CreateLinksAsync_EmptyList_InvokesCallbackWithTrueWithoutHttpCall::test e7ba88352761ca9fd3e956233e3743689b41f47bba09bfbb463c66f132273c70 translated
#[test]
fn create_links_empty_list_invokes_callback_with_true_without_http_call() {
let (client, recording) = client_with_cap(Vec::new());
let callback = Arc::new(Mutex::new(None));
let recorded_result = Arc::clone(&callback);
block_on(client.inventory().create_links(
UUID::random().unwrap(),
Box::new(Vec::<(InventoryItem, String)>::new().into_iter()),
Some(Box::new(move |value| {
*recorded_result.lock().unwrap() = Some(value);
})),
None,
))
.unwrap();
assert_eq!(*callback.lock().unwrap(), Some(true));
assert!(recording.requests().is_empty());
}
// parity-case: LibreMetaverse.Tests/BatchLinkTests.cs::BatchLinkTests.CreateLinksAsync_AisAvailable_SendsSingleBatchPost::test 369d99d37dca818991aca94d6ff9d7881874d74797c7a8b705c5925723e383db translated
#[test]
fn create_links_ais_available_sends_single_batch_post() {
let folder = UUID::random().unwrap();
let first = wearable("Shirt", WearableType::Shirt, AssetType::Clothing);
let second = object("Object");
let response = batch_response(&[
(
UUID::random().unwrap(),
folder,
first.base.base.uuid(),
"Shirt",
),
(
UUID::random().unwrap(),
folder,
second.base.base.uuid(),
"Object",
),
]);
let (client, recording) = client_with_cap(vec![(
format!("{CAP}/category/{}", folder.to_string()),
200,
response,
)]);
block_on(client.inventory().create_links(
folder,
Box::new(vec![(first.base, "400".into()), (second.base, String::new())].into_iter()),
None,
None,
))
.unwrap();
assert_eq!(
recording
.requests()
.iter()
.filter(|request| request.method == "POST")
.count(),
1
);
}
// parity-case: LibreMetaverse.Tests/BatchLinkTests.cs::BatchLinkTests.CreateLinksAsync_AisAvailable_PayloadContainsCorrectFieldsForEachLink::test b7629c3d061fe297e1c1c4f41ca4f8ce9cdeb2764525421be7bdc553a5f83063 translated
#[test]
fn create_links_ais_available_payload_contains_correct_fields_for_each_link() {
let folder = UUID::random().unwrap();
let linked = wearable("Gloves", WearableType::Gloves, AssetType::Clothing);
let linked_id = linked.base.base.uuid();
let response = batch_response(&[(UUID::random().unwrap(), folder, linked_id, "Gloves")]);
let (client, recording) = client_with_cap(vec![(
format!("{CAP}/category/{}", folder.to_string()),
200,
response,
)]);
block_on(client.inventory().create_links(
folder,
Box::new(vec![(linked.base, "900".into())].into_iter()),
None,
None,
))
.unwrap();
let request = recording
.requests()
.into_iter()
.find(|request| request.method == "POST")
.unwrap();
let payload =
OSDParser::deserialize_llsd_xml_with_string(String::from_utf8(request.body).unwrap())
.unwrap();
let OSD::Map(payload) = payload else {
panic!("payload must be a map")
};
let OSD::Array(links) = &payload["links"] else {
panic!("links must be an array")
};
assert_eq!(links.len(), 1);
let OSD::Map(link) = &links[0] else {
panic!("link must be a map")
};
assert!(matches!(&link["linked_id"], OSD::UUID(value) if *value == linked_id));
assert!(matches!(&link["name"], OSD::String(value) if value == "Gloves"));
assert!(matches!(&link["desc"], OSD::String(value) if value == "900"));
assert!(matches!(&link["type"], OSD::Integer(value) if *value == AssetType::Link as i32));
assert!(
matches!(&link["inv_type"], OSD::Integer(value) if *value == i32::from(InventoryType::WEARABLE.0))
);
}
// parity-case: LibreMetaverse.Tests/BatchLinkTests.cs::BatchLinkTests.CreateLinksAsync_AisAvailable_CallbackTrueOnSuccess::test 22dc685f896ec0415e56c1c01846ad90a9757cf5d9a0ed0d3706a76ee325d92d translated
#[test]
fn create_links_ais_available_callback_true_on_success() {
let folder = UUID::random().unwrap();
let linked = object("Hat");
let response = batch_response(&[(
UUID::random().unwrap(),
folder,
linked.base.base.uuid(),
"Hat",
)]);
let (client, _) = client_with_cap(vec![(
format!("{CAP}/category/{}", folder.to_string()),
200,
response,
)]);
let callback = Arc::new(Mutex::new(None));
let result = Arc::clone(&callback);
block_on(client.inventory().create_links(
folder,
Box::new(vec![(linked.base, String::new())].into_iter()),
Some(Box::new(move |value| *result.lock().unwrap() = Some(value))),
None,
))
.unwrap();
assert_eq!(*callback.lock().unwrap(), Some(true));
}
// parity-case: LibreMetaverse.Tests/BatchLinkTests.cs::BatchLinkTests.CreateLinksAsync_ServerError_CallbackFalse::test 5fd9b2967bc61933cea80e01c55ee809f6ade246f10fb4048ec2668e0cc71dbf translated
#[test]
fn create_links_server_error_callback_false() {
let folder = UUID::random().unwrap();
let linked = object("Hat");
let (client, _) = client_with_cap(vec![(
format!("{CAP}/category/{}", folder.to_string()),
500,
String::new(),
)]);
let callback = Arc::new(Mutex::new(None));
let result = Arc::clone(&callback);
block_on(client.inventory().create_links(
folder,
Box::new(vec![(linked.base, String::new())].into_iter()),
Some(Box::new(move |value| *result.lock().unwrap() = Some(value))),
None,
))
.unwrap();
assert_eq!(*callback.lock().unwrap(), Some(false));
}
// parity-case: LibreMetaverse.Tests/BatchLinkTests.cs::BatchLinkTests.GetCategoryLinks_UsesGetWithNoTidParameter::test 3079c03ab4b14d4ac10a410b3685f1593ea267c52de7b6323f9e8e8387314f29 translated
#[test]
fn get_category_links_uses_get_with_no_tid_parameter() {
let folder = UUID::random().unwrap();
let uri = format!("{CAP}/category/{}/links", folder.to_string());
let response = "<llsd><map><key>_embedded</key><map></map></map></llsd>".to_owned();
let (client, recording) = client_with_cap(vec![(uri.clone(), 200, response)]);
let (success, _, _, _) = block_on(
client
.ais_client()
.get_category_links(folder.to_string(), None),
)
.unwrap();
assert!(success);
let requests = recording.requests();
assert_eq!(requests.len(), 1);
assert_eq!(requests[0].method, "GET");
assert_eq!(requests[0].uri.0, uri);
assert!(!requests[0].uri.0.contains("?tid="));
}
// parity-case: LibreMetaverse.Tests/BatchLinkTests.cs::BatchLinkTests.GetCategory_UsesGetWithNoTidParameter::test 98cd0aa58ce9640263d07aecd45337113b07acfc76640d17c5812abe05d35c73 translated
#[test]
fn get_category_uses_get_with_no_tid_parameter() {
let folder = UUID::random().unwrap();
let uri = format!("{CAP}/category/{}", folder.to_string());
let response = "<llsd><map><key>_embedded</key><map></map></map></llsd>".to_owned();
let (client, recording) = client_with_cap(vec![(uri.clone(), 200, response)]);
let (success, _, _, _) =
block_on(client.ais_client().get_category(folder.to_string(), None)).unwrap();
assert!(success);
let requests = recording.requests();
assert_eq!(requests.len(), 1);
assert_eq!(requests[0].method, "GET");
assert_eq!(requests[0].uri.0, uri);
assert!(!requests[0].uri.0.contains("?tid="));
}

View File

@@ -4673,13 +4673,16 @@ impl AppearanceManager {
libremetaverse_types::unimplemented_api!("P:LibreMetaverse.AppearanceManager.ManagerBusy")
}
/// C# member: `P:LibreMetaverse.AppearanceManager.TextureProvider`.
pub fn texture_provider(&self) -> Box<dyn libremetaverse::IBakingTextureProvider> {
pub fn texture_provider(&self) -> std::sync::Arc<dyn libremetaverse::IBakingTextureProvider> {
libremetaverse_types::unimplemented_api!(
"P:LibreMetaverse.AppearanceManager.TextureProvider"
)
}
/// Setter for C# member: `P:LibreMetaverse.AppearanceManager.TextureProvider`.
pub fn set_texture_provider(&mut self, value: Box<dyn libremetaverse::IBakingTextureProvider>) {
pub fn set_texture_provider(
&mut self,
value: std::sync::Arc<dyn libremetaverse::IBakingTextureProvider>,
) {
libremetaverse_types::unimplemented_api!(
"P:LibreMetaverse.AppearanceManager.TextureProvider"
)
@@ -5742,7 +5745,7 @@ pub struct AvatarAppearanceEventArgs;
impl AvatarAppearanceEventArgs {
/// C# member: `M:LibreMetaverse.AvatarAppearanceEventArgs.#ctor(LibreMetaverse.Simulator,LibreMetaverse.UUID,System.Boolean,LibreMetaverse.Primitive.TextureEntryFace,LibreMetaverse.Primitive.TextureEntryFace[],System.Collections.Generic.List{System.Byte},System.Byte,System.Int32,LibreMetaverse.AppearanceFlags,System.Int32)`.
pub fn new(
sim: libremetaverse::Simulator,
sim: Option<libremetaverse::Simulator>,
avatar_id: libremetaverse_types::UUID,
is_trial: bool,
default_texture: libremetaverse::PrimitiveTextureEntryFace,
@@ -6582,32 +6585,34 @@ impl AvatarUpdateEventArgs {
/// C# type: `T:LibreMetaverse.BakeType`.
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
#[repr(i32)]
pub enum BakeType {
#[repr(transparent)]
pub struct BakeType(pub i32);
#[allow(non_upper_case_globals)]
impl BakeType {
/// C# member: `F:LibreMetaverse.BakeType.BakedAux1`.
BakedAux1 = 8,
pub const BakedAux1: Self = Self(8);
/// C# member: `F:LibreMetaverse.BakeType.BakedAux2`.
BakedAux2 = 9,
pub const BakedAux2: Self = Self(9);
/// C# member: `F:LibreMetaverse.BakeType.BakedAux3`.
BakedAux3 = 10,
pub const BakedAux3: Self = Self(10);
/// C# member: `F:LibreMetaverse.BakeType.BakedLeftArm`.
BakedLeftArm = 6,
pub const BakedLeftArm: Self = Self(6);
/// C# member: `F:LibreMetaverse.BakeType.BakedLeftLeg`.
BakedLeftLeg = 7,
pub const BakedLeftLeg: Self = Self(7);
/// C# member: `F:LibreMetaverse.BakeType.Eyes`.
Eyes = 3,
pub const Eyes: Self = Self(3);
/// C# member: `F:LibreMetaverse.BakeType.Hair`.
Hair = 5,
pub const Hair: Self = Self(5);
/// C# member: `F:LibreMetaverse.BakeType.Head`.
Head = 0,
pub const Head: Self = Self(0);
/// C# member: `F:LibreMetaverse.BakeType.LowerBody`.
LowerBody = 2,
pub const LowerBody: Self = Self(2);
/// C# member: `F:LibreMetaverse.BakeType.Skirt`.
Skirt = 4,
pub const Skirt: Self = Self(4);
/// C# member: `F:LibreMetaverse.BakeType.Unknown`.
Unknown = -1,
pub const Unknown: Self = Self(-1);
/// C# member: `F:LibreMetaverse.BakeType.UpperBody`.
UpperBody = 1,
pub const UpperBody: Self = Self(1);
}
/// C# type: `T:LibreMetaverse.BalanceEventArgs`.
@@ -13662,7 +13667,7 @@ impl HttpCapsClientProgressReport {
}
/// C# type: `T:LibreMetaverse.IBakingTextureProvider`.
pub trait IBakingTextureProvider {
pub trait IBakingTextureProvider: std::any::Any {
/// C# member: `M:LibreMetaverse.IBakingTextureProvider.RequestTextureAsync(LibreMetaverse.UUID,System.Threading.CancellationToken)`.
fn request_texture(
&self,
@@ -14865,7 +14870,10 @@ impl InventoryAnimation {
}
/// C# type: `T:LibreMetaverse.InventoryAttachment`.
pub struct InventoryAttachment;
pub struct InventoryAttachment {
/// Rust composition for C# base type `LibreMetaverse.InventoryItem`.
pub base: libremetaverse::InventoryItem,
}
impl InventoryAttachment {
/// C# member: `M:LibreMetaverse.InventoryAttachment.#ctor(LibreMetaverse.UUID)`.
pub fn new(uuid: libremetaverse_types::UUID) -> Result<Self, crate::Error> {
@@ -15086,7 +15094,10 @@ impl InventoryGesture {
}
/// C# type: `T:LibreMetaverse.InventoryItem`.
pub struct InventoryItem;
pub struct InventoryItem {
/// Rust composition for C# base type `LibreMetaverse.InventoryBase`.
pub base: libremetaverse::InventoryBase,
}
impl InventoryItem {
/// C# member: `M:LibreMetaverse.InventoryItem.#ctor(LibreMetaverse.InventoryType,LibreMetaverse.UUID)`.
pub fn new_with_inventory_type_uuid(
@@ -15595,7 +15606,7 @@ impl InventoryManager {
pub async fn create_links(
&self,
folder_id: libremetaverse_types::UUID,
links_to_create: Box<dyn Iterator<Item = (libremetaverse::InventoryBase, String)>>,
links_to_create: Box<dyn Iterator<Item = (libremetaverse::InventoryItem, String)>>,
callback: Option<Box<dyn Fn(bool) + Send + Sync>>,
cancellation_token: Option<libremetaverse_types::compat::CancellationToken>,
) -> Result<(), crate::Error> {
@@ -16935,7 +16946,10 @@ impl InventoryNotecard {
}
/// C# type: `T:LibreMetaverse.InventoryObject`.
pub struct InventoryObject;
pub struct InventoryObject {
/// Rust composition for C# base type `LibreMetaverse.InventoryItem`.
pub base: libremetaverse::InventoryItem,
}
impl InventoryObject {
/// C# member: `M:LibreMetaverse.InventoryObject.#ctor(LibreMetaverse.UUID)`.
pub fn new(uuid: libremetaverse_types::UUID) -> Result<Self, crate::Error> {
@@ -17135,7 +17149,10 @@ impl InventorySound {
}
/// C# type: `T:LibreMetaverse.InventoryTexture`.
pub struct InventoryTexture;
pub struct InventoryTexture {
/// Rust composition for C# base type `LibreMetaverse.InventoryItem`.
pub base: libremetaverse::InventoryItem,
}
impl InventoryTexture {
/// C# member: `M:LibreMetaverse.InventoryTexture.#ctor(LibreMetaverse.UUID)`.
pub fn new(uuid: libremetaverse_types::UUID) -> Result<Self, crate::Error> {
@@ -17146,7 +17163,10 @@ impl InventoryTexture {
}
/// C# type: `T:LibreMetaverse.InventoryWearable`.
pub struct InventoryWearable;
pub struct InventoryWearable {
/// Rust composition for C# base type `LibreMetaverse.InventoryItem`.
pub base: libremetaverse::InventoryItem,
}
impl InventoryWearable {
/// C# member: `M:LibreMetaverse.InventoryWearable.#ctor(LibreMetaverse.UUID)`.
pub fn new(uuid: libremetaverse_types::UUID) -> Result<Self, crate::Error> {
@@ -23495,7 +23515,7 @@ pub struct Primitive {
/// C# member: `F:LibreMetaverse.Primitive.MediaVersion`.
pub media_version: String,
/// C# member: `F:LibreMetaverse.Primitive.NameValues`.
pub name_values: Vec<libremetaverse::NameValue>,
pub name_values: Option<Vec<libremetaverse::NameValue>>,
/// C# member: `F:LibreMetaverse.Primitive.OwnerID`.
pub owner_id: libremetaverse_types::UUID,
/// C# member: `F:LibreMetaverse.Primitive.ParentID`.
@@ -29162,9 +29182,10 @@ pub mod appearance {
/// C# member: `M:LibreMetaverse.Appearance.CompositeCurrentOutfitPolicy.AddPolicy(LibreMetaverse.Appearance.ICurrentOutfitPolicy)`.
pub fn add_policy(
&self,
policy_to_add: Option<Box<dyn libremetaverse::appearance::ICurrentOutfitPolicy>>,
) -> Result<libremetaverse::appearance::CompositeCurrentOutfitPolicy, crate::Error>
{
policy_to_add: Option<
std::sync::Arc<dyn libremetaverse::appearance::ICurrentOutfitPolicy>,
>,
) -> Result<&Self, crate::Error> {
libremetaverse_types::not_implemented(
"M:LibreMetaverse.Appearance.CompositeCurrentOutfitPolicy.AddPolicy(LibreMetaverse.Appearance.ICurrentOutfitPolicy)",
)
@@ -29172,7 +29193,7 @@ pub mod appearance {
/// C# member: `M:LibreMetaverse.Appearance.CompositeCurrentOutfitPolicy.CanAttach(LibreMetaverse.InventoryItem)`.
pub fn can_attach(
&self,
item: Option<libremetaverse::InventoryItem>,
item: Option<std::sync::Arc<libremetaverse::InventoryItem>>,
) -> Result<bool, crate::Error> {
libremetaverse_types::not_implemented(
"M:LibreMetaverse.Appearance.CompositeCurrentOutfitPolicy.CanAttach(LibreMetaverse.InventoryItem)",
@@ -29181,7 +29202,7 @@ pub mod appearance {
/// C# member: `M:LibreMetaverse.Appearance.CompositeCurrentOutfitPolicy.CanDetach(LibreMetaverse.InventoryItem)`.
pub fn can_detach(
&self,
item: Option<libremetaverse::InventoryItem>,
item: Option<std::sync::Arc<libremetaverse::InventoryItem>>,
) -> Result<bool, crate::Error> {
libremetaverse_types::not_implemented(
"M:LibreMetaverse.Appearance.CompositeCurrentOutfitPolicy.CanDetach(LibreMetaverse.InventoryItem)",
@@ -29190,7 +29211,9 @@ pub mod appearance {
/// C# member: `M:LibreMetaverse.Appearance.CompositeCurrentOutfitPolicy.RemovePolicy(LibreMetaverse.Appearance.ICurrentOutfitPolicy)`.
pub fn remove_policy(
&self,
policy_to_remove: Option<Box<dyn libremetaverse::appearance::ICurrentOutfitPolicy>>,
policy_to_remove: Option<
std::sync::Arc<dyn libremetaverse::appearance::ICurrentOutfitPolicy>,
>,
) -> Result<(), crate::Error> {
libremetaverse_types::not_implemented(
"M:LibreMetaverse.Appearance.CompositeCurrentOutfitPolicy.RemovePolicy(LibreMetaverse.Appearance.ICurrentOutfitPolicy)",
@@ -29199,8 +29222,8 @@ pub mod appearance {
/// C# member: `M:LibreMetaverse.Appearance.CompositeCurrentOutfitPolicy.ReportItemChangeAsync(System.Collections.Generic.List{LibreMetaverse.InventoryItem},System.Collections.Generic.List{LibreMetaverse.InventoryItem},System.Threading.CancellationToken)`.
pub async fn report_item_change(
&self,
added_items: Option<Vec<libremetaverse::InventoryItem>>,
removed_items: Option<Vec<libremetaverse::InventoryItem>>,
added_items: Option<std::sync::Arc<Vec<libremetaverse::InventoryItem>>>,
removed_items: Option<std::sync::Arc<Vec<libremetaverse::InventoryItem>>>,
cancellation_token: Option<libremetaverse_types::compat::CancellationToken>,
) -> Result<(), crate::Error> {
libremetaverse_types::not_implemented(
@@ -29214,7 +29237,9 @@ pub mod appearance {
pub struct CurrentOutfitFolder;
impl CurrentOutfitFolder {
/// C# member: `M:LibreMetaverse.Appearance.CurrentOutfitFolder.#ctor(LibreMetaverse.GridClient)`.
pub fn new(client: Option<libremetaverse::GridClient>) -> Result<Self, crate::Error> {
pub fn new(
client: Option<std::sync::Arc<libremetaverse::GridClient>>,
) -> Result<Self, crate::Error> {
libremetaverse_types::not_implemented(
"M:LibreMetaverse.Appearance.CurrentOutfitFolder.#ctor(LibreMetaverse.GridClient)",
)
@@ -29222,9 +29247,11 @@ pub mod appearance {
/// C# member: `M:LibreMetaverse.Appearance.CurrentOutfitFolder.AddPolicy(LibreMetaverse.Appearance.ICurrentOutfitPolicy)`.
pub fn add_policy(
&self,
policy_to_add: Box<dyn libremetaverse::appearance::ICurrentOutfitPolicy>,
) -> Result<Box<dyn libremetaverse::appearance::ICurrentOutfitPolicy>, crate::Error>
{
policy_to_add: std::sync::Arc<dyn libremetaverse::appearance::ICurrentOutfitPolicy>,
) -> Result<
std::sync::Arc<dyn libremetaverse::appearance::ICurrentOutfitPolicy>,
crate::Error,
> {
libremetaverse_types::not_implemented(
"M:LibreMetaverse.Appearance.CurrentOutfitFolder.AddPolicy(LibreMetaverse.Appearance.ICurrentOutfitPolicy)",
)
@@ -29370,7 +29397,7 @@ pub mod appearance {
/// C# member: `M:LibreMetaverse.Appearance.CurrentOutfitFolder.RemovePolicy(LibreMetaverse.Appearance.ICurrentOutfitPolicy)`.
pub fn remove_policy(
&self,
policy_to_remove: Box<dyn libremetaverse::appearance::ICurrentOutfitPolicy>,
policy_to_remove: std::sync::Arc<dyn libremetaverse::appearance::ICurrentOutfitPolicy>,
) -> Result<(), crate::Error> {
libremetaverse_types::not_implemented(
"M:LibreMetaverse.Appearance.CurrentOutfitFolder.RemovePolicy(LibreMetaverse.Appearance.ICurrentOutfitPolicy)",
@@ -29389,8 +29416,8 @@ pub mod appearance {
/// C# member: `M:LibreMetaverse.Appearance.CurrentOutfitFolder.ResolveInventoryLink(LibreMetaverse.InventoryItem)`.
pub fn resolve_inventory_link(
&self,
item_link: libremetaverse::InventoryItem,
) -> Result<Option<libremetaverse::InventoryItem>, crate::Error> {
item_link: std::sync::Arc<libremetaverse::InventoryItem>,
) -> Result<Option<std::sync::Arc<libremetaverse::InventoryItem>>, crate::Error> {
libremetaverse_types::not_implemented(
"M:LibreMetaverse.Appearance.CurrentOutfitFolder.ResolveInventoryLink(LibreMetaverse.InventoryItem)",
)
@@ -29416,11 +29443,11 @@ pub mod appearance {
}
/// C# type: `T:LibreMetaverse.Appearance.ICurrentOutfitPolicy`.
pub trait ICurrentOutfitPolicy {
pub trait ICurrentOutfitPolicy: Send + Sync {
/// C# member: `M:LibreMetaverse.Appearance.ICurrentOutfitPolicy.CanAttach(LibreMetaverse.InventoryItem)`.
fn can_attach(
&self,
item: Option<libremetaverse::InventoryItem>,
item: Option<std::sync::Arc<libremetaverse::InventoryItem>>,
) -> Result<bool, crate::Error> {
libremetaverse_types::not_implemented(
"M:LibreMetaverse.Appearance.ICurrentOutfitPolicy.CanAttach(LibreMetaverse.InventoryItem)",
@@ -29429,7 +29456,7 @@ pub mod appearance {
/// C# member: `M:LibreMetaverse.Appearance.ICurrentOutfitPolicy.CanDetach(LibreMetaverse.InventoryItem)`.
fn can_detach(
&self,
item: Option<libremetaverse::InventoryItem>,
item: Option<std::sync::Arc<libremetaverse::InventoryItem>>,
) -> Result<bool, crate::Error> {
libremetaverse_types::not_implemented(
"M:LibreMetaverse.Appearance.ICurrentOutfitPolicy.CanDetach(LibreMetaverse.InventoryItem)",
@@ -29438,8 +29465,8 @@ pub mod appearance {
/// C# member: `M:LibreMetaverse.Appearance.ICurrentOutfitPolicy.ReportItemChangeAsync(System.Collections.Generic.List{LibreMetaverse.InventoryItem},System.Collections.Generic.List{LibreMetaverse.InventoryItem},System.Threading.CancellationToken)`.
fn report_item_change(
&self,
added_items: Option<Vec<libremetaverse::InventoryItem>>,
removed_items: Option<Vec<libremetaverse::InventoryItem>>,
added_items: Option<std::sync::Arc<Vec<libremetaverse::InventoryItem>>>,
removed_items: Option<std::sync::Arc<Vec<libremetaverse::InventoryItem>>>,
cancellation_token: Option<libremetaverse_types::compat::CancellationToken>,
) -> std::pin::Pin<
Box<dyn std::future::Future<Output = Result<(), crate::Error>> + Send + '_>,
@@ -33904,7 +33931,7 @@ pub mod imaging {
}
/// C# member: `M:LibreMetaverse.Imaging.Baker.AddTexture(LibreMetaverse.AppearanceManager.TextureData)`.
pub fn add_texture(
&self,
&mut self,
tdata: libremetaverse::AppearanceManagerTextureData,
) -> Result<(), crate::Error> {
libremetaverse_types::not_implemented(
@@ -33912,7 +33939,7 @@ pub mod imaging {
)
}
/// C# member: `M:LibreMetaverse.Imaging.Baker.Bake`.
pub fn bake(&self) -> Result<(), crate::Error> {
pub fn bake(&mut self) -> Result<(), crate::Error> {
libremetaverse_types::not_implemented("M:LibreMetaverse.Imaging.Baker.Bake")
}
/// C# member: `M:LibreMetaverse.Imaging.Baker.BakeTypeFor(LibreMetaverse.AvatarTextureIndex)`.

View File

@@ -4,10 +4,14 @@ extern crate self as libremetaverse;
mod generated;
#[cfg(test)]
mod appearance_baker_semantics;
#[cfg(test)]
mod asset_archive_semantics;
#[cfg(test)]
mod avatar_animesh_semantics;
#[cfg(test)]
mod batch_link_internal_semantics;
type Terrain = Vec<Vec<f32>>;
type TerrainLoaded = Box<dyn Fn(Terrain, i64, i64) + Send + Sync>;
@@ -71,6 +75,68 @@ impl animesh::AnimeshPlayer {
}
}
#[allow(dead_code)]
trait CofLinkItem {
fn inventory_item(&self) -> &InventoryItem;
fn wearable_type(&self) -> Option<libremetaverse_types::WearableType>;
}
impl CofLinkItem for InventoryWearable {
fn inventory_item(&self) -> &InventoryItem {
&self.base
}
fn wearable_type(&self) -> Option<libremetaverse_types::WearableType> {
Some(self.wearable_type())
}
}
impl CofLinkItem for InventoryObject {
fn inventory_item(&self) -> &InventoryItem {
&self.base
}
fn wearable_type(&self) -> Option<libremetaverse_types::WearableType> {
None
}
}
#[allow(clippy::unused_self, dead_code)]
impl appearance::CurrentOutfitFolder {
fn get_cof_link_description(&self, _item: &dyn CofLinkItem) -> Result<String, Error> {
libremetaverse_types::not_implemented(
"M:LibreMetaverse.Appearance.CurrentOutfitFolder.GetCofLinkDescription(LibreMetaverse.InventoryItem)",
)
}
}
#[allow(clippy::unused_async, dead_code)]
impl InventoryAISClient {
async fn create_inventory_links(
&self,
_parent_uuid: libremetaverse_types::UUID,
_new_inventory: libremetaverse_structured_data::OSD,
_cancellation_token: Option<libremetaverse_types::compat::CancellationToken>,
) -> Result<Vec<InventoryItem>, Error> {
libremetaverse_types::not_implemented(
"M:LibreMetaverse.InventoryAISClient.CreateInventoryLinksAsync(LibreMetaverse.UUID,LibreMetaverse.StructuredData.OSD,System.Threading.CancellationToken)",
)
}
}
#[allow(dead_code)]
impl imaging::Baker {
fn resize_to_bake_dimensions(
_source: libremetaverse_imaging::ManagedImage,
_width: i32,
_height: i32,
) -> Result<Option<libremetaverse_imaging::ManagedImage>, Error> {
libremetaverse_types::not_implemented(
"M:LibreMetaverse.Imaging.Baker.ResizeToBakeDimensions(LibreMetaverse.Imaging.ManagedImage,System.Int32,System.Int32)",
)
}
}
pub use generated::*;
pub use libremetaverse_imaging as imaging_abstractions;
pub use libremetaverse_structured_data as structured_data;